1 /*
2  * hunt-proton: AMQP Protocol library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.proton.codec.WritableBuffer;
13 
14 import hunt.proton.codec.ReadableBuffer;
15 import hunt.Exceptions;
16 import hunt.io.ByteBuffer;
17 import hunt.io.BufferUtils;
18 import std.stdio;
19 
20 abstract class WritableBuffer {
21     void put(byte b);
22 
23     void putFloat(float f);
24 
25     void putDouble(double d);
26 
27     void put(byte[] src, int offset, int length);
28 
29     void putShort(short s);
30 
31     void putInt(int i);
32 
33     void putLong(long l);
34 
35     bool hasRemaining();
36 
37     //default void ensureRemaining(int requiredRemaining) {
38     //    // No-op to allow for drop in updates
39     //}
40     void ensureRemaining(int requiredRemaining) {
41         // No-op to allow for drop in updates
42     }
43 
44     int remaining();
45 
46     int position();
47 
48     void position(int position);
49 
50     void put(ByteBuffer payload);
51 
52     void put(ReadableBuffer payload);
53 
54     void put(string value) {
55         int length = cast(int)value.length;
56 
57         for (int i = 0; i < length; i++) {
58             int c = value[i];
59             if ((c & 0xFF80) == 0) {
60                 // U+0000..U+007F
61                 put(cast(byte) c);
62             } else if ((c & 0xF800) == 0)  {
63                 // U+0080..U+07FF
64                 put(cast(byte) (0xC0 | ((c >> 6) & 0x1F)));
65                 put(cast(byte) (0x80 | (c & 0x3F)));
66             } else if ((c & 0xD800) != 0xD800 || (c > 0xDBFF)) {
67                 // U+0800..U+FFFF - excluding surrogate pairs
68                 put(cast(byte) (0xE0 | ((c >> 12) & 0x0F)));
69                 put(cast(byte) (0x80 | ((c >> 6) & 0x3F)));
70                 put(cast(byte) (0x80 | (c & 0x3F)));
71             } else {
72                 int low;
73 
74                 if ((++i == length) || ((low = value[i]) & 0xDC00) != 0xDC00) {
75                     throw new IllegalArgumentException("String contains invalid Unicode code points");
76                 }
77 
78                 c = 0x010000 + ((c & 0x03FF) << 10) + (low & 0x03FF);
79 
80                 put(cast(byte) (0xF0 | ((c >> 18) & 0x07)));
81                 put(cast(byte) (0x80 | ((c >> 12) & 0x3F)));
82                 put(cast(byte) (0x80 | ((c >> 6) & 0x3F)));
83                 put(cast(byte) (0x80 | (c & 0x3F)));
84             }
85         }
86     }
87 
88     int limit();
89 
90 }
91 
92 
93 class ByteBufferWrapper : WritableBuffer {
94     private ByteBuffer _buf;
95 
96     this(ByteBuffer buf) {
97         _buf = buf;
98     }
99 
100     override
101     public void put(byte b) {
102         _buf.put(b);
103     }
104 
105     override
106     public void putFloat(float f) {
107         _buf.putInt(cast(int)f);
108     }
109 
110     override
111     public void putDouble(double d) {
112         _buf.putLong(cast(long)d);
113     }
114 
115     override
116     public void put(byte[] src, int offset, int length) {
117         _buf.put(src, offset, length);
118     }
119 
120     override
121     public void putShort(short s) {
122         _buf.putShort(s);
123     }
124 
125     override
126     public void putInt(int i) {
127         _buf.putInt(i);
128     }
129 
130     override
131     public void putLong(long l) {
132         _buf.putLong(l);
133     }
134 
135     override
136     public bool hasRemaining() {
137         return _buf.hasRemaining();
138     }
139 
140     override
141     public void ensureRemaining(int remaining) {
142         if (remaining < 0) {
143             throw new IllegalArgumentException("Required remaining bytes cannot be negative");
144         }
145 
146         if (_buf.remaining() < remaining) {
147             writefln("....... %d  ..... %d",_buf.remaining(),remaining);
148             //IndexOutOfBoundsException cause = new IndexOutOfBoundsException(String.format(
149             //    "Requested min remaining bytes(%d) exceeds remaining(%d) in underlying ByteBuffer: %s",
150             //    remaining, _buf.remaining(), _buf));
151 
152             throw (new BufferOverflowException());
153         }
154     }
155 
156     override
157     public int remaining() {
158         return _buf.remaining();
159     }
160 
161     override
162     public int position() {
163         return _buf.position();
164     }
165 
166     override
167     public void position(int position) {
168         _buf.position(position);
169     }
170 
171     override
172     public void put(ByteBuffer src) {
173         _buf.put(src);
174     }
175 
176     override
177     public void put(ReadableBuffer src) {
178         src.get(this);
179     }
180 
181     override
182     public void put(string value) {
183         int length = cast(int)value.length;
184 
185         int pos = _buf.position();
186 
187         for (int i = 0; i < length; i++) {
188             int c = value[i];
189             try {
190                 if ((c & 0xFF80) == 0) {
191                     // U+0000..U+007F
192                     put(pos++, cast(byte) c);
193                 } else if ((c & 0xF800) == 0)  {
194                     // U+0080..U+07FF
195                     put(pos++, cast(byte) (0xC0 | ((c >> 6) & 0x1F)));
196                     put(pos++, cast(byte) (0x80 | (c & 0x3F)));
197                 } else if ((c & 0xD800) != 0xD800 || (c > 0xDBFF))  {
198                     // U+0800..U+FFFF - excluding surrogate pairs
199                     put(pos++, cast(byte) (0xE0 | ((c >> 12) & 0x0F)));
200                     put(pos++, cast(byte) (0x80 | ((c >> 6) & 0x3F)));
201                     put(pos++, cast(byte) (0x80 | (c & 0x3F)));
202                 } else {
203                     int low;
204 
205                     if ((++i == length) || ((low = value[i]) & 0xDC00) != 0xDC00) {
206                         throw new IllegalArgumentException("String contains invalid Unicode code points");
207                     }
208 
209                     c = 0x010000 + ((c & 0x03FF) << 10) + (low & 0x03FF);
210 
211                     put(pos++, cast(byte) (0xF0 | ((c >> 18) & 0x07)));
212                     put(pos++, cast(byte) (0x80 | ((c >> 12) & 0x3F)));
213                     put(pos++, cast(byte) (0x80 | ((c >> 6) & 0x3F)));
214                     put(pos++, cast(byte) (0x80 | (c & 0x3F)));
215                 }
216             }
217             catch(IndexOutOfBoundsException ioobe) {
218                 throw new BufferOverflowException();
219             }
220         }
221 
222         // Now move the buffer position to reflect the work done here
223         _buf.position(pos);
224     }
225 
226     override
227     public int limit() {
228         return _buf.limit();
229     }
230 
231     public ByteBuffer byteBuffer() {
232         return _buf;
233     }
234 
235     public ReadableBuffer toReadableBuffer() {
236         return ByteBufferReader.wrap(cast(ByteBuffer) _buf.duplicate().flip());
237     }
238 
239     //override
240     //public string toString() {
241     //    return String.format("[pos: %d, limit: %d, remaining:%d]", _buf.position(), _buf.limit(), _buf.remaining());
242     //}
243 
244     public static ByteBufferWrapper allocate(int size) {
245         ByteBuffer allocated = BufferUtils.allocate(size);
246         return new ByteBufferWrapper(allocated);
247     }
248 
249     public static ByteBufferWrapper wrap(ByteBuffer buffer) {
250         return new ByteBufferWrapper(buffer);
251     }
252 
253     public static ByteBufferWrapper wrap(byte[] bytes) {
254         return new ByteBufferWrapper(BufferUtils.toBuffer(bytes));
255     }
256 
257     private void put(int index, byte value) {
258         if (_buf.hasArray()) {
259             _buf.array()[_buf.arrayOffset() + index] = value;
260         } else {
261             _buf.put(index, value);
262         }
263     }
264 }