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.StringType;
13 
14 import hunt.proton.codec.SmallFloatingSizePrimitiveTypeEncoding;
15 import hunt.proton.codec.TypeEncoding;
16 import hunt.proton.codec.EncodingCodes;
17 import hunt.proton.codec.EncoderImpl;
18 import hunt.proton.codec.PrimitiveTypeEncoding;
19 import hunt.proton.codec.ReadableBuffer;
20 import hunt.proton.codec.AbstractPrimitiveType;
21 import hunt.proton.codec.DecoderImpl;
22 import hunt.collection.Collection;
23 import hunt.Exceptions;
24 import hunt.String;
25 import hunt.collection.ArrayList;
26 
27 import hunt.proton.codec.LargeFloatingSizePrimitiveTypeEncoding;
28 import hunt.logging;
29 
30 
31 interface StringEncoding : PrimitiveTypeEncoding!(String)
32 {
33     void setValue(String val, int length);
34 }
35 
36 
37 class StringType : AbstractPrimitiveType!(String)
38 {
39     private static DecoderImpl.TypeDecoder!(String) _stringCreator ;
40 
41     private StringEncoding _stringEncoding;
42     private StringEncoding _shortStringEncoding;
43 
44     this(EncoderImpl encoder, DecoderImpl decoder)
45     {
46         StringType._stringCreator = new class DecoderImpl.TypeDecoder!(String)
47         {
48             override
49             public String decode(DecoderImpl decoder, ReadableBuffer buffer)
50             {
51                 //  CharsetDecoder charsetDecoder = decoder.getCharsetDecoder();
52                 try
53                 {
54                     return new String(buffer.readUTF8());
55                 }
56                 catch (CharacterCodingException e)
57                 {
58                     throw new IllegalArgumentException("Cannot parse String");
59                 }
60                 //finally
61                 //{
62                 //    charsetDecoder.reset();
63                 //}
64             }
65         };
66         _stringEncoding = new AllStringEncoding(encoder, decoder);
67         _shortStringEncoding = new ShortStringEncoding(encoder, decoder);
68         encoder.register(typeid(String), this);
69         decoder.register(this);
70     }
71 
72     public TypeInfo getTypeClass()
73     {
74         return typeid(String);
75     }
76 
77     public ITypeEncoding getEncoding(Object val)
78     {
79         int length = calculateUTF8Length(cast(String)val);
80         StringEncoding encoding = length <= 255
81                 ? _shortStringEncoding
82                 : _stringEncoding;
83         encoding.setValue(cast(String)val, length);
84         return encoding;
85     }
86 
87     static int calculateUTF8Length(String str)
88     {
89         string s = str.value;
90        // int stringLength = cast(int)((cast(byte[])s).length);
91 
92         // ASCII Optimized length case
93         //int utf8len = stringLength;
94         //int processed = 0;
95         //for (; processed < stringLength && s[processed] < 0x80; processed++) {}
96         //
97         //if (processed < stringLength)
98         //{
99         //    // Non-ASCII length remainder
100         //    utf8len = extendedCalculateUTF8Length(str, processed, stringLength, utf8len);
101         //}
102 
103         return cast(int)(s.length);
104     }
105 
106     static int extendedCalculateUTF8Length(String str, int index, int length, int utf8len) {
107         string s = str.value;
108         for (; index < length; index++)
109         {
110             int c = s[index];
111             if ((c & 0xFF80) != 0)         /* U+0080..    */
112             {
113                 utf8len++;
114                 if(((c & 0xF800) != 0))    /* U+0800..    */
115                 {
116                     utf8len++;
117                     // surrogate pairs should always combine to create a code point with a 4 octet representation
118                     if ((c & 0xD800) == 0xD800 && c < 0xDC00)
119                     {
120                         index++;
121                     }
122                 }
123             }
124         }
125 
126         return utf8len;
127     }
128 
129     public StringEncoding getCanonicalEncoding()
130     {
131         return _stringEncoding;
132     }
133 
134     public Collection!(TypeEncoding!(String)) getAllEncodings()
135     {
136         ArrayList!(TypeEncoding!(String)) lst = new ArrayList!(TypeEncoding!(String))();
137         lst.add(_shortStringEncoding);
138         lst.add(_stringEncoding);
139         return lst;
140     }
141 
142     //Collection!(PrimitiveTypeEncoding!(String)) getAllEncodings()
143     //{
144     //    return super.getAllEncodings();
145     //}
146 
147 
148     class AllStringEncoding
149             : LargeFloatingSizePrimitiveTypeEncoding!(String)
150             , StringEncoding
151     {
152         private String _value;
153         private int _length;
154 
155         this(EncoderImpl encoder, DecoderImpl decoder)
156         {
157             super(encoder, decoder);
158         }
159 
160         override
161         protected void writeEncodedValue(String val)
162         {
163             getEncoder().getBuffer().ensureRemaining(getEncodedValueSize(val));
164             getEncoder().writeRaw(val);
165         }
166 
167         override
168         protected int getEncodedValueSize(String val)
169         {
170             return (val == _value) ? _length : calculateUTF8Length(val);
171         }
172 
173         override
174         public byte getEncodingCode()
175         {
176             return EncodingCodes.STR32;
177         }
178 
179         override
180         public StringType getType()
181         {
182             return this.outer;
183         }
184 
185         override
186         public bool encodesSuperset(TypeEncoding!(String) encoding)
187         {
188             return (getType() == encoding.getType());
189         }
190 
191         override
192         public Object readValue()
193         {
194             DecoderImpl decoder = getDecoder();
195             int size = decoder.readRawInt();
196             return size == 0 ? new String("") : decoder.readRaw(_stringCreator, size);
197         }
198 
199         override
200         public void setValue(String val, int length)
201         {
202             _value = val;
203             _length = length;
204         }
205 
206         override
207         public void skipValue()
208         {
209             DecoderImpl decoder = getDecoder();
210             ReadableBuffer buffer = decoder.getBuffer();
211             int size = decoder.readRawInt();
212             buffer.position(buffer.position() + size);
213         }
214 
215         override bool encodesJavaPrimitive()
216         {
217             return super.encodesJavaPrimitive();
218         }
219 
220         override TypeInfo getTypeClass()
221         {
222             return super.getTypeClass();
223         }
224 
225         override void writeConstructor()
226         {
227             return super.writeConstructor();
228         }
229 
230         override  int getConstructorSize()
231         {
232             return super.getConstructorSize();
233         }
234     }
235 
236     class ShortStringEncoding
237             : SmallFloatingSizePrimitiveTypeEncoding!(String)
238             , StringEncoding
239     {
240         private String _value;
241         private int _length;
242 
243         this(EncoderImpl encoder, DecoderImpl decoder)
244         {
245             super(encoder, decoder);
246         }
247 
248         override
249         protected void writeEncodedValue(String val)
250         {
251             getEncoder().getBuffer().ensureRemaining(getEncodedValueSize(val));
252             getEncoder().writeRaw(val);
253         }
254 
255         override
256         protected int getEncodedValueSize(String val)
257         {
258           //  logInfof("size : %d",(val == _value) ? _length : calculateUTF8Length(val));
259             return (val == _value) ? _length : calculateUTF8Length(val);
260         }
261 
262         override
263         public byte getEncodingCode()
264         {
265             return EncodingCodes.STR8;
266         }
267 
268         override
269         public StringType getType()
270         {
271             return this.outer;
272         }
273 
274         override
275         public bool encodesSuperset(TypeEncoding!(String) encoder)
276         {
277             return encoder == this;
278         }
279 
280         override
281         public Object readValue()
282         {
283             DecoderImpl decoder = getDecoder();
284             int size = (cast(int)decoder.readRawByte()) & 0xff;
285             String a;
286             if (size == 0)
287             {
288                 a = new String("");
289             }
290             else
291             {
292                 a =decoder.readRaw(_stringCreator, size);
293             }
294             //return size == 0 ? new String("") : decoder.readRaw(_stringCreator, size);
295            // logInfof("dddddddddddddddddd  %s",a);
296             return a;
297         }
298 
299         override
300         public void setValue(String val, int length)
301         {
302             _value = val;
303             _length = length;
304         }
305 
306         override
307         public void skipValue()
308         {
309             DecoderImpl decoder = getDecoder();
310             ReadableBuffer buffer = decoder.getBuffer();
311             int size = (cast(int)decoder.readRawByte()) & 0xff;
312             buffer.position(buffer.position() + size);
313         }
314 
315         override bool encodesJavaPrimitive()
316         {
317             return super.encodesJavaPrimitive();
318         }
319 
320         override TypeInfo getTypeClass()
321         {
322             return super.getTypeClass();
323         }
324 
325         override void writeConstructor()
326         {
327             return super.writeConstructor();
328         }
329 
330         override  int getConstructorSize()
331         {
332             return super.getConstructorSize();
333         }
334     }
335 }