1 /**********************************************************\
2 |                                                          |
3 |                          hprose                          |
4 |                                                          |
5 | Official WebSite: http://www.hprose.com/                 |
6 |                   http://www.hprose.org/                 |
7 |                                                          |
8 \**********************************************************/
9 
10 /**********************************************************\
11  *                                                        *
12  * hprose/io/bytes.d                                      *
13  *                                                        *
14  * hprose bytes io library for D.                         *
15  *                                                        *
16  * LastModified: Mar 11, 2016                             *
17  * Author: Ma Bingyao <andot@hprose.com>                  *
18  *                                                        *
19 \**********************************************************/
20 
21 module hprose.io.bytes;
22 
23 @trusted:
24 
25 import hprose.io.tags;
26 import std.algorithm;
27 import std.bigint;
28 import std.conv;
29 import std.stdio;
30 import std.string;
31 import std.traits;
32 
33 class BytesIO {
34     private {
35         char[] _buffer;
36         int _pos;
37     }
38 
39     @property {
40         int size() {
41             return cast(int)_buffer.length;
42         }
43         bool eof() {
44             return _pos >= size;
45         }
46         immutable(ubyte)[] buffer() {
47             return cast(immutable(ubyte)[])_buffer;
48         }
49     }
50 
51     this() {
52         this("");
53     }
54     this(BytesIO data) {
55         init(data.buffer);
56     }
57     this(string data) {
58         init(data);
59     }
60     this(ubyte[] data) {
61         init(data);
62     }
63     void init(T)(T data) {
64         _buffer = cast(char[])data;
65         _pos = 0;
66     }
67     void close() {
68         _buffer.length = 0;
69         _pos = 0;
70     }
71     char read() {
72         if (size > _pos) {
73             return _buffer[_pos++];
74         }
75         else {
76             throw new Exception("no byte found in stream");
77         }
78     }
79     char[] read(int n) {
80         char[] bytes = _buffer[_pos .. _pos + n];
81         _pos += n;
82         return bytes;
83     }
84     char[] readFull() {
85         char[] bytes = _buffer[_pos .. $];
86         _pos = size;
87         return bytes;
88     }
89     char[] readUntil(T...)(T tags) {
90         long count = countUntil(_buffer[_pos .. $], tags);
91         if (count < 0) return readFull();
92         char[] bytes = _buffer[_pos .. _pos + count];
93         _pos += count + 1;
94         return bytes;
95     }
96     char[] readBytes(T...)(T tags) {
97         long count = countUntil(_buffer[_pos .. $], tags);
98         if (count < 0) return readFull();
99         count++;
100         char[] bytes = _buffer[_pos .. _pos + count];
101         _pos += count;
102         return bytes;
103     }
104     char skipUntil(T...)(T tags) {
105         auto count = countUntil(_buffer[_pos .. $], tags);
106         if (count < 0) throw new Exception("does not find tags in stream");
107         char result = _buffer[_pos + count];
108         _pos += count + 1;
109         return result;
110     }
111     string readUTF8Char() {
112         int pos = _pos;
113         ubyte tag = read();
114         switch (tag >> 4) {
115             case 0: .. case 7: break;
116             case 12, 13: ++_pos; break;
117             case 14: _pos += 2; break;
118             default: throw new Exception("bad utf-8 encoding");
119         }
120         if (_pos > size) throw new Exception("bad utf-8 encoding");
121         return cast(string)_buffer[pos .. _pos];
122     }
123     T readString(T = string)(int wlen) if (isSomeString!T) {
124         int pos = _pos;
125         for (int i = 0; i < wlen; ++i) {
126             ubyte tag = read();
127             switch (tag >> 4) {
128                 case 0: .. case 7: break;
129                 case 12, 13: ++_pos; break;
130                 case 14: _pos += 2; break;
131                 case 15: _pos += 3; ++i; break;
132                 default: throw new Exception("bad utf-8 encoding");
133             }
134         }
135         if (_pos > size) throw new Exception("bad utf-8 encoding");
136         return cast(T)_buffer[pos .. _pos];
137     }
138     T readInt(T = int)(char tag) if (isSigned!T) {
139         int c = read();
140         if (c == tag) return 0;
141         T result = 0;
142         int len = size;
143         T sign = 1;
144         switch (c) {
145             case TagNeg: sign = -1; goto case TagPos;
146             case TagPos: c = read(); goto default;
147             default: break;
148         }
149         while (_pos < len && c != tag) {
150             result *= 10;
151             result += (c - '0') * sign;
152             c = read();
153         }
154         return result;
155     }
156     T readInt(T)(char tag) if (isUnsigned!T) {
157         return cast(T)readInt!(Signed!T)(tag);
158     }
159     void skip(int n) {
160         _pos += n;
161     }
162     BytesIO write(in char[] data) {
163         if (data.length > 0) {
164             _buffer ~= data;
165         }
166         return this;
167     }
168     BytesIO write(in byte[] data) {
169         return write(cast(char[])data);
170     }
171     BytesIO write(in ubyte[] data) {
172         return write(cast(char[])data);
173     }
174     BytesIO write(BytesIO data) {
175         return write(cast(char[])data.buffer);
176     }
177     BytesIO write(T)(in T x) {
178         static if (is(T == char)) {
179             _buffer ~= x;
180         }
181         else static if (is(T == ubyte) || is(T == byte)) {
182             _buffer ~= cast(char)x;
183         }
184         else static if (isIntegral!T ||
185             isSomeChar!T ||
186             is(T == float)) {
187             _buffer ~= cast(char[])to!string(x);
188         }
189         else static if (is(T == double) ||
190             is(T == real)) {
191             _buffer ~= cast(char[])format("%.16g", x);
192         }
193         return this;
194     }
195     override string toString() {
196         return cast(string)_buffer;
197     }
198 }
199 
200 unittest {
201     BytesIO bytes = new BytesIO("i123;d3.14;");
202     assert(bytes.readUntil(';') == "i123");
203     assert(bytes.readUntil(';') == "d3.14");
204     bytes.write("hello");
205     assert(bytes.read(5) == "hello");
206     const int i = 123456789;
207     bytes.write(i).write(';');
208     assert(bytes.readInt(';') == i);
209     bytes.write(1).write('1').write(';');
210     assert(bytes.readInt(';') == 11);
211     const float f = 3.14159265;
212     bytes.write(f).write(';');
213     assert(bytes.readUntil(';') == "3.14159");
214     const double d = 3.141592653589793238;
215     bytes.write(d).write(';');
216     assert(bytes.readUntil(';') == "3.141592653589793");
217     const real r = 3.141592653589793238;
218     bytes.write(r).write(';');
219     assert(bytes.readUntil(';', '.') == "3");
220     assert(bytes.skipUntil(';', '.') == ';');
221     bytes.write("你好啊");
222     assert(bytes.readString(3) == "你好啊");
223 }