root/trunk/tango/scrapple/protocol/PlainTextProtocol.d

Revision 39, 19.3 kB (checked in by flithm, 8 months ago)

Fixes for Tango 0.99.4

Line 
1 /*******************************************************************************
2
3         copyright:      Copyright (c) 2007 Matthias Walter. All rights reserved
4
5         license:        BSD style: $(LICENSE)
6
7         version:        June 2007 : initial release
8         
9         author:         Xammy
10
11 *******************************************************************************/
12
13 module tango.scrapple.io.protocol.PlainTextProtocol;
14
15 private import tango.io.protocol.model.IProtocol;
16 private import tango.io.Buffer;
17 private import tango.io.Conduit;
18 private import tango.text.Util;
19 private import tango.core.Exception;
20 private import Integer = tango.text.convert.Integer;
21 private import Float = tango.text.convert.Float;
22
23 private import tango.io.Stdout;
24
25 /*******************************************************************************
26
27     This protocol is for compatibility to a widely used mechanism
28     for saving data to files and reading it back with methods
29     like printf and scanf.
30
31     Therefore, integer numbers and floats are converted to their
32     string representations. Data is written in tokens
33     which are seperated with whitespace.
34     Except strings, which are written as one token,
35     all arrays are writting as one token for one array element.
36     
37     The member prefix determines whether arrays are preceded
38     by their length. If not, the user must save it as a token of its own.
39
40     Suppose you have a file of 4 integer vectors, each one
41     containing 3 integers. One might save it this way:
42     ---
43     4 3
44
45      1  2  3
46      4  5  6
47      7  8  9
48     10 11 12
49     ---
50     To read them, simply attach an instance to a Buffer and your
51     Reader / Writer to it.
52
53     ---
54     auto file = new FileConduit ("vectors.txt");
55     auto buffer = new Buffer (file);
56
57     // 2nd arg is false, because each vector is not preceeded by it's length
58     auto protocol = new PlainTextProtocol (buffer, false);
59
60     auto reader = new Reader (protocol);
61
62     int numOfVectors, vectorSize;
63     int[][] vectorArray;
64
65     reader (numOfVectors) (vectorSize);
66
67     for (int i = 0; i < numOfVectors; i++)
68     {
69         int[] vec = new int [vectorSize];
70
71         // number of tokens to read is given via vec.length
72         reader (vec);
73
74         vectorArray ~= vec;
75     }
76     ---
77
78 *******************************************************************************/
79
80 class PlainTextProtocol : IProtocol
81 {
82     protected IBuffer buffer_; // Buffer, we're  associated to
83     uint precision = 6; /// Precision, floating point data is saved with.
84     bool prefix; /// Whether arrays are preceeded by their size
85
86     /***************************************************************************
87
88         Construct a buffer upon the given buffer
89
90     ***************************************************************************/
91
92     this (IBuffer buffer, bool _prefix = true)
93     {
94         buffer_ = buffer;
95         prefix = _prefix;
96     }
97    
98     /***************************************************************************
99
100         Return the buffer associated with this reader
101
102     ***************************************************************************/
103
104     IBuffer buffer ()
105     {
106         return buffer_;
107     }
108
109     /***************************************************************************
110
111         Internal write method. You should not use it directly.
112         Take a look at the Reader class instead.
113
114     ***************************************************************************/
115
116     void write (void* src, uint bytes, Type type)
117     {
118         char[] suffix = " ";
119
120         switch (type)
121         {
122         case Type.Void:
123         case Type.Obj:
124         case Type.Pointer:
125             break;
126         case Type.Utf8:
127             if (! isSpace (* cast(char*) src))
128                 buffer_.append (src, bytes);
129             else
130                 suffix = "\n";
131             break;
132         case Type.Utf16:
133             if (! isSpace (* cast(wchar*) src))
134                 buffer_.append (src, bytes);
135             else
136                 suffix = "\n";
137             break;
138         case Type.Utf32:
139             if (! isSpace (* cast(dchar*) src))
140                 buffer_.append (src, bytes);
141             else
142                 suffix = "\n";
143             break;
144         case Type.Bool:
145             buffer_.append (*(cast(bool*)src) ? "true" : "false");
146             break;
147         case Type.Byte:
148         case Type.UByte:
149         case Type.Short:
150         case Type.UShort:
151         case Type.Int:
152         case Type.UInt:
153         case Type.Long:
154         case Type.ULong:
155             writeInteger (src, type);
156         break;
157         case Type.Float:
158         case Type.Double:
159         case Type.Real:
160             writeFloat (src, type);
161         break;
162         }
163         buffer_.append (suffix);
164         buffer_.flush ();
165     }
166
167     /***************************************************************************
168
169         Internal writeArray method. You should not use it directly.
170         Take a look at the Reader class insteadt.
171
172     ***************************************************************************/
173
174     void writeArray (void* src, uint bytes, Type type)
175     {
176         switch (type)
177         {
178         case Type.Void:
179         case Type.Obj:
180         case Type.Pointer:
181             return;
182         case Type.Utf8:
183         case Type.Utf16:
184         case Type.Utf32:
185             write (src, bytes, type);
186             break;
187         case Type.Bool:
188         case Type.Byte:
189         case Type.UByte:
190         case Type.Short:
191         case Type.UShort:
192         case Type.Int:
193         case Type.UInt:
194         case Type.Long:
195         case Type.ULong:
196         case Type.Float:
197         case Type.Double:
198         case Type.Real:
199             writeTokenArray (src, bytes, type);
200         break;
201         }
202     }
203    
204     /***************************************************************************
205
206         Internal read method. You should not use it directly.
207         Take a look at the Reader class insteadt.
208
209     ***************************************************************************/
210
211     void[] read (void* dst, uint bytes, Type type)
212     {
213         switch (type)
214         {
215         case Type.Void:
216         case Type.Obj:
217         case Type.Pointer:
218             return null;
219         case Type.Utf8:
220         case Type.Utf16:
221         case Type.Utf32:
222             char[] temp = cast(char[]) dst[0 .. bytes];
223             char[] token = readToken (temp);
224             if (token.length != bytes)
225                 throw new IOException ("\"" ~ token ~ "\" is not a valid character");
226             return token;
227         case Type.Bool:
228             char[5] temp = void;
229             char[] token = readToken (temp);
230             if (token == "true")
231                 *cast(bool*)dst = true;
232             else if (token == "false")
233                 *cast(bool*)dst = false;
234             else
235                 throw new IOException ("\"" ~ token ~ "\" is not a valid bool");
236             return dst[0 .. bytes];
237         case Type.Byte:
238         case Type.UByte:
239         case Type.Short:
240         case Type.UShort:
241         case Type.Int:
242         case Type.UInt:
243         case Type.Long:
244         case Type.ULong:
245             return readInteger (dst, bytes, type);
246         case Type.Float:
247         case Type.Double:
248         case Type.Real:
249             return readFloat (dst, bytes, type);
250         }
251     }
252    
253     /***************************************************************************
254
255         Internal readArray method. You should not use it directly.
256         Take a look at the Reader class insteadt.
257
258     ***************************************************************************/
259
260     void[] readArray (void* dst, uint bytes, Type type, Allocator alloc)
261     {
262         char[] temp = cast(char[]) dst[0 .. bytes];
263         char[] token = void;
264
265         switch (type)
266         {
267         case Type.Void:
268             return null;
269         case Type.Utf8:
270         case Type.Utf16:
271         case Type.Utf32:
272         case Type.Obj:
273         case Type.Pointer:
274             return readToken (temp);
275         case Type.Bool:
276         case Type.Byte:
277         case Type.UByte:
278         case Type.Short:
279         case Type.UShort:
280         case Type.Int:
281         case Type.UInt:
282         case Type.Long:
283         case Type.ULong:
284         case Type.Float:
285         case Type.Double:
286         case Type.Real:
287             return readTokenArray (dst, bytes, type);
288         }
289         return null;
290     }
291
292     /***************************************************************************
293
294         Writes an integer *src of type to buffer.
295
296     ***************************************************************************/
297
298     protected void writeInteger (void* src, Type type)
299     {
300         char[66] temp;
301
302         switch (type)
303         {
304             case Type.Byte:
305                 buffer_.append ( Integer.format (temp, * cast (byte *) src, Integer.Style.Signed) );
306             break;
307             case Type.UByte:
308                 buffer_.append ( Integer.format (temp, * cast (ubyte *) src, Integer.Style.Unsigned) );
309             break;
310             case Type.Short:
311                 buffer_.append ( Integer.format (temp, * cast (short *) src, Integer.Style.Signed) );
312             break;
313             case Type.UShort:
314                 buffer_.append ( Integer.format (temp, * cast (ushort *) src, Integer.Style.Unsigned) );
315             break;
316             case Type.Int:
317                 buffer_.append ( Integer.format (temp, * cast (int *) src, Integer.Style.Signed) );
318             break;
319             case Type.UInt:
320                 buffer_.append ( Integer.format (temp, * cast (uint *) src, Integer.Style.Unsigned) );
321             break;
322             case Type.Long:
323                 buffer_.append ( Integer.format (temp, * cast (long *) src, Integer.Style.Signed) );
324             break;
325             case Type.ULong:
326                 buffer_.append ( Integer.format (temp, * cast (ulong *) src, Integer.Style.Unsigned) );
327             break;
328             default:
329                 assert (false);
330         }
331     }
332    
333     /***************************************************************************
334
335         Writes a float *src of type T to buffer.
336
337     ***************************************************************************/
338
339     protected void writeFloat (void* src, Type type)
340     {
341         char[66] temp;
342
343         switch (type)
344         {
345             case Type.Float:
346                 buffer_.append ( Float.format (temp, * cast (float *) src, precision) );
347             break;
348             case Type.Double:
349                 buffer_.append ( Float.format (temp, * cast (double *) src, precision) );
350             break;
351             case Type.Real:
352                 buffer_.append ( Float.format (temp, * cast (real *) src, precision) );
353             break;
354             default:
355                 assert (false);
356         }
357     }
358    
359     /***************************************************************************
360
361         Returns size of type.
362
363     ***************************************************************************/
364
365     protected ubyte typeSize (Type type)
366     {
367         switch (type)
368         {
369             case Type.Bool:
370             case Type.Byte:
371             case Type.UByte:
372                 return 1;
373             case Type.Short:
374             case Type.UShort:
375                 return 2;
376             case Type.Int:
377             case Type.UInt:
378             case Type.Float:
379                 return 4;
380             case Type.Long:
381             case Type.ULong:
382             case Type.Double:
383                 return 8;
384             case Type.Real:
385                 return 12;
386             default:
387                 assert (false);
388                 return 0;
389         }
390     }
391
392     /***************************************************************************
393
394         Writes an array token by token.
395
396     ***************************************************************************/
397
398     protected void writeTokenArray (void* src, uint bytes, Type type)
399     {
400         uint each = typeSize (type);
401         uint size = bytes / each;
402         if (prefix)
403             write (&size, uint.sizeof, Type.UInt);
404         for (int i = 0; i < size; i++)
405         {
406             write (src + i * each, each, type);
407         }
408     }
409
410     /***************************************************************************
411
412         Reads one token by searching for whitespace / newline / eof
413
414     ***************************************************************************/
415
416     protected char[] readToken (char[] temp)
417     {
418         size_t position = 0;
419         bool onHeap = false;
420         char[1] s;
421
422         while (buffer_.read (s) != IConduit.Eof)
423         {
424             char c = s[0];
425             if (isSpace(c))
426             {
427                 // token starts with whitespace - skip
428                 if (position == 0)
429                     continue;
430                 return temp[0 .. position];
431             }
432             else
433             {
434                 if (position >= temp.length)
435                 {
436                     if (!onHeap)
437                     {
438                         temp = temp.dup;
439                         onHeap = true;
440                     }
441                     temp.length = 2*temp.length+8;
442                 }
443                 temp[position++] = c;
444             }
445         }
446         return temp[0 .. position];
447     }
448    
449     /***************************************************************************
450
451         Reads an integer from the buffer.
452
453     ***************************************************************************/
454
455     protected void[] readInteger (void* dst, uint bytes, Type type)
456     {
457         uint len;
458         char[66] temp = void;
459         char[] token = readToken (temp);
460
461         switch (type)
462         {
463             case Type.Byte:
464                 *(cast (byte *) dst) = Integer.parse (token, 10, &len);
465             break;
466             case Type.UByte:
467                 *(cast (ubyte *) dst) = Integer.parse (token, 10, &len);
468             break;
469             case Type.Short:
470                 *(cast (short *) dst) = Integer.parse (token, 10, &len);
471             break;
472             case Type.UShort:
473                 *(cast (ushort *) dst) = Integer.parse (token, 10, &len);
474             break;
475             case Type.Int:
476                 *(cast (int *) dst) = Integer.parse (token, 10, &len);
477             break;
478             case Type.UInt:
479                 *(cast (uint *) dst) = Integer.parse (token, 10, &len);
480             break;
481             case Type.Long:
482                 *(cast (long *) dst) = Integer.parse (token, 10, &len);
483             break;
484             case Type.ULong:
485                 *(cast (ulong *) dst) = Integer.parse (token, 10, &len);
486             break;
487             default:
488                 assert (false);
489         }
490         if (len != token.length)
491             throw new IOException ("\"" ~ token ~ "\" is not a valid integer");
492         return dst[0 .. bytes];
493     }
494    
495     /***************************************************************************
496
497         Reads a float from the buffer.
498
499     ***************************************************************************/
500
501     protected void[] readFloat (void* dst, uint bytes, Type type)
502     {
503         uint len;
504         char[66] temp = void;
505         char[] token = readToken (temp);
506
507         switch (type)
508         {
509             case Type.Float:
510                 * cast (float *) dst = Float.parse (token, &len);
511             break;
512             case Type.Double:
513                 * cast (double *) dst = Float.parse (token, &len);
514             break;
515             case Type.Real:
516                 * cast (real *) dst = Float.parse (token, &len);
517             break;
518             default:
519                 assert (false);
520         }
521         if (len != token.length)
522             throw new IOException ("\"" ~ token ~ "\" is not a valid float");
523         return dst[0 .. bytes];
524     }
525    
526     /***************************************************************************
527
528         Reads an array of tokens and calls read for each.
529         The size is calculated by the formula  bytes / T.sizeof
530
531     ***************************************************************************/
532
533     protected void[] readTokenArray (void* dst, uint bytes, Type type)
534     {
535         uint each = typeSize (type);
536         uint size = void;
537
538         void[] result;
539         if (prefix)
540         {
541             read (&size, uint.sizeof, Type.UInt);
542             result = new void [size * each];
543         }
544         else
545         {
546             size = bytes / each;
547             result = dst[0 .. bytes];
548         }
549            
550         for (int i=0; i<size; i++)
551         {
552             read (result.ptr + (i * each), each, type);
553         }
554
555         return result;
556     }
557 } 
558
559
560
561 /*******************************************************************************
562
563 *******************************************************************************/
564
565 debug (UnitTest)
566 {
567     import tango.io.protocol.Reader;
568     import tango.io.protocol.Writer;
569     import tango.text.convert.Integer;
570
571     unittest
572     {
573         byte b = byte.min;
574         ubyte ub = ubyte.max;
575         short s = short.min;
576         ushort us = ushort.max;
577         int i = int.min;
578         uint ui = uint.max;
579         long l = long.min;
580         ulong ul = ulong.max;
581         float f = 3.14159f;
582         double d = 3.14159;
583         real r = 3.14159;
584         char c = 'c';
585         wchar wc = 'w';
586         dchar dc = 'd';
587         char[] ac = "char[]";
588         wchar[] awc = "wchar[]";
589         dchar[] adc = "dchar[]";
590         int[] ai = [1, 2, 3];
591
592         auto buffer = new Buffer (1024);
593         auto protocol = new PlainTextProtocol (buffer);
594         auto output = new Writer (protocol);
595         auto input = new Reader (protocol);
596
597         output (b) (ub) (s) (us) (i) (ui) (l) (ul) ('\n') (f) (d) (r) ('\n') (c) (wc) (dc) ('\n') (ac) (awc) (adc) ('\n');
598         assert (protocol.prefix == true);
599         output (ai) ('\n');
600         protocol.prefix = false;
601         output (ai) ('\n');
602
603         assert (buffer.slice[0 .. 118] == "-128 255 -32768 65535 -2147483648 4294967295 -9223372036854775808 18446744073709551615 \n3.141590 3.141590 3.141590 \nc "c);
604     &n