blob: 4a75af8b7f0cbd24a7d1c6eb38cdf8cb4fd4f1b0 [file] [log] [blame]
Jens Geyerd5436f52014-10-03 19:50:38 +02001(*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *)
19
20unit Thrift.Utils;
21
22interface
23
Jens Geyer9f7f11e2016-04-14 21:37:11 +020024{$I Thrift.Defines.inc}
Nick4f5229e2016-04-14 16:43:22 +030025
Jens Geyerd5436f52014-10-03 19:50:38 +020026uses
Jens Geyer9f7f11e2016-04-14 21:37:11 +020027 {$IFDEF OLD_UNIT_NAMES}
Jens Geyer8f7487e2019-05-09 22:21:32 +020028 Classes, Windows, SysUtils, Character, SyncObjs, TypInfo, Rtti;
Jens Geyer9f7f11e2016-04-14 21:37:11 +020029 {$ELSE}
Jens Geyer8f7487e2019-05-09 22:21:32 +020030 System.Classes, Winapi.Windows, System.SysUtils, System.Character,
31 System.SyncObjs, System.TypInfo, System.Rtti;
Jens Geyer9f7f11e2016-04-14 21:37:11 +020032 {$ENDIF}
Jens Geyerd5436f52014-10-03 19:50:38 +020033
34type
Jens Geyer8f7487e2019-05-09 22:21:32 +020035 ISupportsToString = interface
36 ['{AF71C350-E0CD-4E94-B77C-0310DC8227FF}']
37 function ToString : string;
38 end;
39
40
Jens Geyerd5436f52014-10-03 19:50:38 +020041 IOverlappedHelper = interface
42 ['{A1832EFA-2E02-4884-8F09-F0A0277157FA}']
43 function Overlapped : TOverlapped;
44 function OverlappedPtr : POverlapped;
45 function WaitHandle : THandle;
46 function WaitFor(dwTimeout: DWORD) : DWORD;
47 end;
48
49 TOverlappedHelperImpl = class( TInterfacedObject, IOverlappedHelper)
50 strict protected
51 FOverlapped : TOverlapped;
52 FEvent : TEvent;
53
54 // IOverlappedHelper
55 function Overlapped : TOverlapped;
56 function OverlappedPtr : POverlapped;
57 function WaitHandle : THandle;
58 function WaitFor(dwTimeout: DWORD) : DWORD;
59 public
60 constructor Create;
61 destructor Destroy; override;
62 end;
63
64
Jens Geyer8f7487e2019-05-09 22:21:32 +020065 TThriftStringBuilder = class( TStringBuilder)
66 public
67 function Append(const Value: TBytes): TStringBuilder; overload;
68 function Append(const Value: ISupportsToString): TStringBuilder; overload;
69 end;
70
71
Jens Geyerd8bddbc2014-12-14 00:41:33 +010072 Base64Utils = class sealed
73 public
74 class function Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer; static;
75 class function Decode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer; static;
76 end;
77
78
Jens Geyer71070432016-01-29 10:08:39 +010079 CharUtils = class sealed
80 public
81 class function IsHighSurrogate( const c : Char) : Boolean; static; inline;
82 class function IsLowSurrogate( const c : Char) : Boolean; static; inline;
Jens Geyer589ee5b2021-03-29 21:40:55 +020083
84 class function IsHtmlDoctype( const fourBytes : Integer) : Boolean; static;
Jens Geyer71070432016-01-29 10:08:39 +010085 end;
86
Jens Geyer8f7487e2019-05-09 22:21:32 +020087 EnumUtils<T> = class sealed
88 public
89 class function ToString(const value : Integer) : string; reintroduce; static; inline;
90 end;
91
92 StringUtils<T> = class sealed
93 public
94 class function ToString(const value : T) : string; reintroduce; static; inline;
95 end;
96
Jens Geyer71070432016-01-29 10:08:39 +010097
Jens Geyer83ff7532019-06-06 22:46:03 +020098const
99 THRIFT_MIMETYPE = 'application/x-thrift';
100
Jens Geyerf7904452017-07-26 15:02:12 +0200101{$IFDEF Win64}
Jens Geyer6e2a6982019-12-12 23:07:49 +0100102function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64;
Jens Geyerf7904452017-07-26 15:02:12 +0200103{$ENDIF}
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200104
Jens Geyer71070432016-01-29 10:08:39 +0100105
Jens Geyerd5436f52014-10-03 19:50:38 +0200106implementation
107
Jens Geyerd5436f52014-10-03 19:50:38 +0200108{ TOverlappedHelperImpl }
109
110constructor TOverlappedHelperImpl.Create;
111begin
112 inherited Create;
113 FillChar( FOverlapped, SizeOf(FOverlapped), 0);
114 FEvent := TEvent.Create( nil, TRUE, FALSE, ''); // always ManualReset, see MSDN
115 FOverlapped.hEvent := FEvent.Handle;
116end;
117
118
119
120destructor TOverlappedHelperImpl.Destroy;
121begin
122 try
123 FOverlapped.hEvent := 0;
124 FreeAndNil( FEvent);
125
126 finally
127 inherited Destroy;
128 end;
129
130end;
131
132
133function TOverlappedHelperImpl.Overlapped : TOverlapped;
134begin
135 result := FOverlapped;
136end;
137
138
139function TOverlappedHelperImpl.OverlappedPtr : POverlapped;
140begin
141 result := @FOverlapped;
142end;
143
144
145function TOverlappedHelperImpl.WaitHandle : THandle;
146begin
147 result := FOverlapped.hEvent;
148end;
149
150
151function TOverlappedHelperImpl.WaitFor( dwTimeout : DWORD) : DWORD;
152begin
153 result := WaitForSingleObject( FOverlapped.hEvent, dwTimeout);
154end;
155
156
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100157{ Base64Utils }
158
159class function Base64Utils.Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
160const ENCODE_TABLE : PAnsiChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
161begin
162 ASSERT( len in [1..3]);
163 dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shr 2) and $3F]);
164 case len of
165 3 : begin
166 Inc(dstOff);
167 dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
168 Inc(dstOff);
169 dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff + 1] shl 2) and $3C) or ((src[srcOff + 2] shr 6) and $03)]);
170 Inc(dstOff);
171 dst[dstOff] := Byte( ENCODE_TABLE[ src[srcOff + 2] and $3F]);
172 result := 4;
173 end;
174
175 2 : begin
176 Inc(dstOff);
177 dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
178 Inc(dstOff);
179 dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff + 1] shl 2) and $3C]);
180 result := 3;
181 end;
182
183 1 : begin
184 Inc(dstOff);
185 dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shl 4) and $30]);
186 result := 2;
187 end;
188
189 else
190 ASSERT( FALSE);
Jens Geyer9f9535c2014-12-14 04:16:05 +0100191 result := 0; // because invalid call
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100192 end;
193end;
194
195
196class function Base64Utils.Decode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
197const DECODE_TABLE : array[0..$FF] of Integer
198 = ( -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
199 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
200 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
201 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
202 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
203 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
204 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
205 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
206 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
207 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
208 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
209 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
210 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
211 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
212 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
213 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 );
214begin
215 ASSERT( len in [1..4]);
216 result := 1;
217 dst[dstOff] := ((DECODE_TABLE[src[srcOff] and $0FF] shl 2)
218 or (DECODE_TABLE[src[srcOff + 1] and $0FF] shr 4));
219
220 if (len > 2) then begin
221 Inc( result);
222 Inc( dstOff);
223 dst[dstOff] := (((DECODE_TABLE[src[srcOff + 1] and $0FF] shl 4) and $F0)
224 or (DECODE_TABLE[src[srcOff + 2] and $0FF] shr 2));
225
226 if (len > 3) then begin
227 Inc( result);
228 Inc( dstOff);
229 dst[dstOff] := (((DECODE_TABLE[src[srcOff + 2] and $0FF] shl 6) and $C0)
230 or DECODE_TABLE[src[srcOff + 3] and $0FF]);
231 end;
232 end;
233end;
Jens Geyerd5436f52014-10-03 19:50:38 +0200234
235
Jens Geyer71070432016-01-29 10:08:39 +0100236class function CharUtils.IsHighSurrogate( const c : Char) : Boolean;
237begin
Jens Geyer36c0b342018-01-19 19:17:33 +0100238 {$IF CompilerVersion < 25.0}
239 {$IFDEF OLD_UNIT_NAMES}
240 result := Character.IsHighSurrogate(c);
241 {$ELSE}
242 result := System.Character.IsHighSurrogate(c);
243 {$ENDIF}
Jens Geyer71070432016-01-29 10:08:39 +0100244 {$ELSE}
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200245 result := c.IsHighSurrogate();
Jens Geyer71070432016-01-29 10:08:39 +0100246 {$IFEND}
247end;
248
249
250class function CharUtils.IsLowSurrogate( const c : Char) : Boolean;
251begin
Jens Geyer36c0b342018-01-19 19:17:33 +0100252 {$IF CompilerVersion < 25.0}
253 {$IFDEF OLD_UNIT_NAMES}
254 result := Character.IsLowSurrogate(c);
255 {$ELSE}
256 result := System.Character.IsLowSurrogate(c);
257 {$ENDIF}
Jens Geyer71070432016-01-29 10:08:39 +0100258 {$ELSE}
Jens Geyer36c0b342018-01-19 19:17:33 +0100259 result := c.IsLowSurrogate();
Jens Geyer71070432016-01-29 10:08:39 +0100260 {$IFEND}
261end;
262
263
Jens Geyer589ee5b2021-03-29 21:40:55 +0200264class function CharUtils.IsHtmlDoctype( const fourBytes : Integer) : Boolean;
265var pc : PAnsiChar;
266const HTML_BEGIN : PAnsiChar = 'OD!<'; // first 4 bytes of '<!DOCTYPE ' in LE byte order
267begin
268 pc := @fourBytes;
269
270 if UpCase(pc^) <> HTML_BEGIN[0]
271 then Exit(FALSE);
272
273 Inc( pc);
274 if UpCase(pc^) <> HTML_BEGIN[1]
275 then Exit(FALSE);
276
277
278 Inc( pc);
279 if UpCase(pc^) <> HTML_BEGIN[2]
280 then Exit(FALSE);
281
282 Inc( pc);
283 result := (UpCase(pc^) = HTML_BEGIN[3]);
284end;
285
286
287
Jens Geyerf7904452017-07-26 15:02:12 +0200288{$IFDEF Win64}
289
290function InterlockedCompareExchange64( var Target : Int64; Exchange, Comparand : Int64) : Int64; inline;
291begin
292 {$IFDEF OLD_UNIT_NAMES}
293 result := Windows.InterlockedCompareExchange64( Target, Exchange, Comparand);
294 {$ELSE}
295 result := WinApi.Windows.InterlockedCompareExchange64( Target, Exchange, Comparand);
296 {$ENDIF}
297end;
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200298
299
Jens Geyerf7904452017-07-26 15:02:12 +0200300function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64;
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200301var old : Int64;
302begin
303 repeat
304 Old := Addend;
305 until (InterlockedCompareExchange64( Addend, Old + Value, Old) = Old);
306 result := Old;
307end;
308
Jens Geyerf7904452017-07-26 15:02:12 +0200309{$ENDIF}
Jens Geyer71070432016-01-29 10:08:39 +0100310
311
Jens Geyer8f7487e2019-05-09 22:21:32 +0200312{ EnumUtils<T> }
313
314class function EnumUtils<T>.ToString(const value : Integer) : string;
315var pType : PTypeInfo;
316begin
317 pType := PTypeInfo(TypeInfo(T));
Jens Geyer6e2a6982019-12-12 23:07:49 +0100318 if Assigned(pType)
319 and (pType^.Kind = tkEnumeration)
320 {$IF CompilerVersion >= 23.0} // TODO: Range correct? What we know is that XE does not offer it, but Rio has it
321 and (pType^.TypeData^.MaxValue >= value)
322 and (pType^.TypeData^.MinValue <= value)
323 {$ELSE}
324 and FALSE // THRIFT-5048: pType^.TypeData^ member not supported -> prevent GetEnumName() from reading outside the legal range
325 {$IFEND}
326 then result := GetEnumName( PTypeInfo(pType), value)
Jens Geyer8f7487e2019-05-09 22:21:32 +0200327 else result := IntToStr(Ord(value));
328end;
329
330
331{ StringUtils<T> }
332
333class function StringUtils<T>.ToString(const value : T) : string;
Jens Geyer85431d92019-05-10 21:17:00 +0200334type PInterface = ^IInterface;
Jens Geyer8f7487e2019-05-09 22:21:32 +0200335var pType : PTypeInfo;
Jens Geyer85431d92019-05-10 21:17:00 +0200336 stos : ISupportsToString;
337 pIntf : PInterface; // Workaround: Rio does not allow the direct typecast
Jens Geyer8f7487e2019-05-09 22:21:32 +0200338begin
339 pType := PTypeInfo(TypeInfo(T));
340 if Assigned(pType) then begin
341 case pType^.Kind of
Jens Geyerec572712020-07-13 21:15:31 +0200342
Jens Geyer8f7487e2019-05-09 22:21:32 +0200343 tkInterface : begin
Jens Geyer85431d92019-05-10 21:17:00 +0200344 pIntf := PInterface(@value);
345 if Supports( pIntf^, ISupportsToString, stos) then begin
346 result := stos.toString;
Jens Geyer8f7487e2019-05-09 22:21:32 +0200347 Exit;
348 end;
349 end;
Jens Geyerec572712020-07-13 21:15:31 +0200350
351 tkEnumeration : begin
352 case SizeOf(value) of
353 1 : begin result := EnumUtils<T>.ToString( PShortInt(@value)^); Exit; end;
354 2 : begin result := EnumUtils<T>.ToString( PSmallInt(@value)^); Exit; end;
355 4 : begin result := EnumUtils<T>.ToString( PLongInt(@value)^); Exit; end;
356 else
357 ASSERT(FALSE); // in theory, this should not happen
358 end;
359 end;
360
Jens Geyer8f7487e2019-05-09 22:21:32 +0200361 end;
362 end;
363
364 result := TValue.From<T>(value).ToString;
365end;
366
367
368{ TThriftStringBuilder }
369
370function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder;
371begin
372 Result := Append( string( RawByteString(Value)) );
373end;
374
375function TThriftStringBuilder.Append( const Value: ISupportsToString): TStringBuilder;
376begin
377 Result := Append( Value.ToString );
378end;
379
380
Jens Geyerd5436f52014-10-03 19:50:38 +0200381end.