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 module hunt.proton.engine.impl.FrameWriterBuffer;
12 
13 import hunt.io.ByteBuffer;
14 import std.algorithm;
15 import hunt.proton.codec.ReadableBuffer;
16 import hunt.proton.codec.WritableBuffer;
17 import hunt.Byte;
18 import hunt.Short;
19 import hunt.Long;
20 import hunt.Integer;
21 import hunt.Float;
22 import hunt.Double;
23 import hunt.Exceptions;
24 import hunt.logging;
25 class FrameWriterBuffer : WritableBuffer {
26 
27     public static int DEFAULT_CAPACITY = 1024;
28 
29     byte [] _array;
30     int _position;
31 
32    /**
33     * Creates a new WritableBuffer with default capacity.
34     */
35    this() {
36        this(DEFAULT_CAPACITY);
37    }
38 
39     /**
40      * Create a new WritableBuffer with the given capacity.
41      *
42      * @param capacity
43      *      the inital capacity to allocate for this buffer.
44      */
45     this(int capacity) {
46         this._array = new byte[capacity];
47     }
48 
49     public byte[] array() {
50         return _array;
51     }
52 
53     public int arrayOffset() {
54         return 0;
55     }
56 
57     override
58     public void put(byte b) {
59         ensureRemaining(Byte.BYTES);
60         _array[_position++] = b;
61     }
62 
63     override
64     public void putShort(short value) {
65         ensureRemaining(Short.BYTES);
66         _array[_position++] = cast(byte)(value >>> 8);
67         _array[_position++] = cast(byte)(value >>> 0);
68     }
69 
70     override
71     public void putInt(int value) {
72         ensureRemaining(Integer.BYTES);
73         _array[_position++] = cast(byte)(value >>> 24);
74         _array[_position++] = cast(byte)(value >>> 16);
75         _array[_position++] = cast(byte)(value >>> 8);
76         _array[_position++] = cast(byte)(value >>> 0);
77     }
78 
79     override
80     public void putLong(long value) {
81         ensureRemaining(Long.BYTES);
82         _array[_position++] = cast(byte)(value >>> 56);
83         _array[_position++] = cast(byte)(value >>> 48);
84         _array[_position++] = cast(byte)(value >>> 40);
85         _array[_position++] = cast(byte)(value >>> 32);
86         _array[_position++] = cast(byte)(value >>> 24);
87         _array[_position++] = cast(byte)(value >>> 16);
88         _array[_position++] = cast(byte)(value >>> 8);
89         _array[_position++] = cast(byte)(value >>> 0);
90     }
91 
92     override
93     public void putFloat(float value) {
94         putInt(Float.floatToRawIntBits(value));
95     }
96 
97     override
98     public void putDouble(double value) {
99         putLong(Double.doubleToRawLongBits(value));
100     }
101 
102     override
103     public void put(byte[] src, int offset, int length) {
104         if (length == 0) {
105             return;
106         }
107 
108         ensureRemaining(length);
109         //System.arraycopy(src, offset, array, position, length);
110         _array[_position .. _position+length] = src[offset .. offset+length];
111         _position += length;
112     }
113 
114     override
115     public void put(ByteBuffer payload) {
116         int toCopy = payload.remaining();
117         ensureRemaining(toCopy);
118 
119         if (payload.hasArray()) {
120             //System.arraycopy(payload.array(), payload.arrayOffset() + payload.position(), array, position, toCopy);
121             _array[_position .. _position+toCopy] = payload.array()[payload.arrayOffset() + payload.position() .. payload.arrayOffset() + payload.position()+toCopy];
122             payload.position(payload.position() + toCopy);
123         } else {
124             payload.get(_array, _position, toCopy);
125         }
126 
127         _position += toCopy;
128     }
129 
130     override
131     public void put(ReadableBuffer payload) {
132         int toCopy = payload.remaining();
133         ensureRemaining(toCopy);
134 
135         if (payload.hasArray()) {
136             //System.arraycopy(payload.array(), payload.arrayOffset() + payload.position(), array, position, toCopy);
137             _array[_position .. toCopy+ _position] = payload.array()[payload.arrayOffset() + payload.position() .. payload.arrayOffset() + payload.position() + toCopy];
138             payload.position(payload.position() + toCopy);
139         } else {
140             payload.get(_array, _position, toCopy);
141         }
142 
143         _position += toCopy;
144     }
145 
146     override
147     public bool hasRemaining() {
148         return _position < Integer.MAX_VALUE;
149     }
150 
151     override
152     public int remaining() {
153         return Integer.MAX_VALUE - _position;
154     }
155 
156     /**
157      * Ensures the the buffer has at least the requiredRemaining space specified.
158      * <p>
159      * The internal buffer will be doubled if the requested capacity is less than that
160      * amount or the buffer will be expanded to the full new requiredRemaining value.
161      *
162      * @param requiredRemaining
163      *      the minimum remaining bytes needed to meet the next write operation.
164      */
165     override
166     public void ensureRemaining(int requiredRemaining) {
167         if (requiredRemaining > _array.length - _position) {
168             byte [] newBuffer = new byte[max(_array.length << 1, requiredRemaining + _position)];
169             newBuffer[0 .. _array.length] = _array[0 .. _array.length];
170             //System.arraycopy(array, 0, newBuffer, 0, array.length);
171             _array = newBuffer;
172         }
173     }
174 
175     override
176     public int position() {
177         return _position;
178     }
179 
180     override
181     public void position(int position) {
182         if (position < 0) {
183             throw new IllegalArgumentException("Requested new buffer position cannot be negative");
184         }
185 
186         if (position > _array.length) {
187             ensureRemaining(position - cast(int)_array.length);
188         }
189 
190         this._position = position;
191     }
192 
193     override
194     public int limit() {
195         return Integer.MAX_VALUE;
196     }
197 
198     /**
199      * Copy bytes from this buffer into the target buffer and compacts this buffer.
200      * <p>
201      * Copy either all bytes written into this buffer (start to current position) or
202      * as many as will fit if the target capacity is less that the bytes written.  Bytes
203      * not read from this buffer are moved to the front of the buffer and the position is
204      * reset to the end of the copied region.
205      *
206      * @param target
207      *      The array to move bytes to from those written into this buffer.
208      *
209      * @return the number of bytes transfered to the target buffer.
210      */
211     public int transferTo(ByteBuffer target) {
212         int size = min(_position, target.remaining());
213         if (size == 0) {
214             return 0;
215         }
216 
217         if (target.hasArray()) {
218            // System.arraycopy(array, 0, target.array(), target.arrayOffset() + target.position(), size);
219             target.array()[target.arrayOffset() + target.position() .. target.arrayOffset() + target.position()+size] = _array[0 .. size];
220             target.position(target.position() + size);
221         } else {
222             target.put(_array, 0, size);
223         }
224 
225         // Compact any remaining data to the front of the array so that new writes can reuse
226         // space previously allocated and not extend the array if possible.
227         if (size != _position) {
228             int remainder = _position - size;
229 
230             //System.arraycopy(array, size, array, 0, remainder);
231             // _array[0 .. remainder] = _array[size .. _position].dup;
232             for(int i =0; i< remainder; i++) {
233                 _array[i] = _array[size+i];
234             }
235 
236             _position = remainder;  // ensure we are at end of unread chunk
237         } else {
238             _position = 0; // reset to empty state.
239         }
240 
241         return size;
242     }
243 }