blob: 122653572e74c3d71a56ca549bf375bebc8704f2 [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 Geyer62445c12022-06-29 00:00:00 +020087
88 IntegerUtils = class sealed
89 strict private
90 class procedure SwapBytes( var one, two : Byte); static; inline;
91 class procedure Swap2( const pValue : Pointer); static;
92 class procedure Swap4( const pValue : Pointer); static;
93 class procedure Swap8( const pValue : Pointer); static;
94 public
95 class procedure SwapByteOrder( const pValue : Pointer; const size : Integer); overload; static;
96 end;
97
98
99 TGuidHelper = record helper for System.TGuid
100 public
101 function SwapByteOrder : TGuid;
102
103 {$IFDEF Debug}
104 class procedure SelfTest; static;
105 {$ENDIF}
106 end;
107
108
Jens Geyer8f7487e2019-05-09 22:21:32 +0200109 EnumUtils<T> = class sealed
110 public
111 class function ToString(const value : Integer) : string; reintroduce; static; inline;
112 end;
113
Jens Geyer62445c12022-06-29 00:00:00 +0200114
Jens Geyer8f7487e2019-05-09 22:21:32 +0200115 StringUtils<T> = class sealed
116 public
117 class function ToString(const value : T) : string; reintroduce; static; inline;
118 end;
119
Jens Geyer71070432016-01-29 10:08:39 +0100120
Jens Geyer83ff7532019-06-06 22:46:03 +0200121const
122 THRIFT_MIMETYPE = 'application/x-thrift';
123
Jens Geyerf7904452017-07-26 15:02:12 +0200124{$IFDEF Win64}
Jens Geyer6e2a6982019-12-12 23:07:49 +0100125function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64;
Jens Geyerf7904452017-07-26 15:02:12 +0200126{$ENDIF}
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200127
Jens Geyer71070432016-01-29 10:08:39 +0100128
Jens Geyerd5436f52014-10-03 19:50:38 +0200129implementation
130
Jens Geyerd5436f52014-10-03 19:50:38 +0200131{ TOverlappedHelperImpl }
132
133constructor TOverlappedHelperImpl.Create;
134begin
135 inherited Create;
136 FillChar( FOverlapped, SizeOf(FOverlapped), 0);
137 FEvent := TEvent.Create( nil, TRUE, FALSE, ''); // always ManualReset, see MSDN
138 FOverlapped.hEvent := FEvent.Handle;
139end;
140
141
142
143destructor TOverlappedHelperImpl.Destroy;
144begin
145 try
146 FOverlapped.hEvent := 0;
147 FreeAndNil( FEvent);
148
149 finally
150 inherited Destroy;
151 end;
152
153end;
154
155
156function TOverlappedHelperImpl.Overlapped : TOverlapped;
157begin
158 result := FOverlapped;
159end;
160
161
162function TOverlappedHelperImpl.OverlappedPtr : POverlapped;
163begin
164 result := @FOverlapped;
165end;
166
167
168function TOverlappedHelperImpl.WaitHandle : THandle;
169begin
170 result := FOverlapped.hEvent;
171end;
172
173
174function TOverlappedHelperImpl.WaitFor( dwTimeout : DWORD) : DWORD;
175begin
176 result := WaitForSingleObject( FOverlapped.hEvent, dwTimeout);
177end;
178
179
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100180{ Base64Utils }
181
182class function Base64Utils.Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
183const ENCODE_TABLE : PAnsiChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
184begin
185 ASSERT( len in [1..3]);
186 dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shr 2) and $3F]);
187 case len of
188 3 : begin
189 Inc(dstOff);
190 dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
191 Inc(dstOff);
192 dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff + 1] shl 2) and $3C) or ((src[srcOff + 2] shr 6) and $03)]);
193 Inc(dstOff);
194 dst[dstOff] := Byte( ENCODE_TABLE[ src[srcOff + 2] and $3F]);
195 result := 4;
196 end;
197
198 2 : begin
199 Inc(dstOff);
200 dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
201 Inc(dstOff);
202 dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff + 1] shl 2) and $3C]);
203 result := 3;
204 end;
205
206 1 : begin
207 Inc(dstOff);
208 dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shl 4) and $30]);
209 result := 2;
210 end;
211
212 else
213 ASSERT( FALSE);
Jens Geyer9f9535c2014-12-14 04:16:05 +0100214 result := 0; // because invalid call
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100215 end;
216end;
217
218
219class function Base64Utils.Decode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
220const DECODE_TABLE : array[0..$FF] of Integer
221 = ( -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
222 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
223 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
224 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
225 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
226 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
227 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
228 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
229 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
230 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
231 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
232 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
233 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
234 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
235 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
236 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 );
237begin
238 ASSERT( len in [1..4]);
239 result := 1;
240 dst[dstOff] := ((DECODE_TABLE[src[srcOff] and $0FF] shl 2)
241 or (DECODE_TABLE[src[srcOff + 1] and $0FF] shr 4));
242
243 if (len > 2) then begin
244 Inc( result);
245 Inc( dstOff);
246 dst[dstOff] := (((DECODE_TABLE[src[srcOff + 1] and $0FF] shl 4) and $F0)
247 or (DECODE_TABLE[src[srcOff + 2] and $0FF] shr 2));
248
249 if (len > 3) then begin
250 Inc( result);
251 Inc( dstOff);
252 dst[dstOff] := (((DECODE_TABLE[src[srcOff + 2] and $0FF] shl 6) and $C0)
253 or DECODE_TABLE[src[srcOff + 3] and $0FF]);
254 end;
255 end;
256end;
Jens Geyerd5436f52014-10-03 19:50:38 +0200257
258
Jens Geyer71070432016-01-29 10:08:39 +0100259class function CharUtils.IsHighSurrogate( const c : Char) : Boolean;
260begin
Jens Geyer36c0b342018-01-19 19:17:33 +0100261 {$IF CompilerVersion < 25.0}
262 {$IFDEF OLD_UNIT_NAMES}
263 result := Character.IsHighSurrogate(c);
264 {$ELSE}
265 result := System.Character.IsHighSurrogate(c);
266 {$ENDIF}
Jens Geyer71070432016-01-29 10:08:39 +0100267 {$ELSE}
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200268 result := c.IsHighSurrogate();
Jens Geyer71070432016-01-29 10:08:39 +0100269 {$IFEND}
270end;
271
272
273class function CharUtils.IsLowSurrogate( const c : Char) : Boolean;
274begin
Jens Geyer36c0b342018-01-19 19:17:33 +0100275 {$IF CompilerVersion < 25.0}
276 {$IFDEF OLD_UNIT_NAMES}
277 result := Character.IsLowSurrogate(c);
278 {$ELSE}
279 result := System.Character.IsLowSurrogate(c);
280 {$ENDIF}
Jens Geyer71070432016-01-29 10:08:39 +0100281 {$ELSE}
Jens Geyer36c0b342018-01-19 19:17:33 +0100282 result := c.IsLowSurrogate();
Jens Geyer71070432016-01-29 10:08:39 +0100283 {$IFEND}
284end;
285
286
Jens Geyer589ee5b2021-03-29 21:40:55 +0200287class function CharUtils.IsHtmlDoctype( const fourBytes : Integer) : Boolean;
288var pc : PAnsiChar;
289const HTML_BEGIN : PAnsiChar = 'OD!<'; // first 4 bytes of '<!DOCTYPE ' in LE byte order
290begin
291 pc := @fourBytes;
292
293 if UpCase(pc^) <> HTML_BEGIN[0]
294 then Exit(FALSE);
295
296 Inc( pc);
297 if UpCase(pc^) <> HTML_BEGIN[1]
298 then Exit(FALSE);
299
300
301 Inc( pc);
302 if UpCase(pc^) <> HTML_BEGIN[2]
303 then Exit(FALSE);
304
305 Inc( pc);
306 result := (UpCase(pc^) = HTML_BEGIN[3]);
307end;
308
Jens Geyer62445c12022-06-29 00:00:00 +0200309{ IntegerUtils }
310
311
312class procedure IntegerUtils.SwapBytes( var one, two : Byte);
313var tmp : Byte;
314begin
315 tmp := one;
316 one := two;
317 two := tmp;
318end;
319
320
321class procedure IntegerUtils.Swap2( const pValue : Pointer);
322var pData : PByteArray absolute pValue;
323begin
324 SwapBytes( pData^[0], pData^[1]);
325end;
326
327
328class procedure IntegerUtils.Swap4( const pValue : Pointer);
329var pData : PByteArray absolute pValue;
330begin
331 SwapBytes( pData^[0], pData^[3]);
332 SwapBytes( pData^[1], pData^[2]);
333end;
334
335
336class procedure IntegerUtils.Swap8( const pValue : Pointer);
337var pData : PByteArray absolute pValue;
338begin
339 SwapBytes( pData^[0], pData^[7]);
340 SwapBytes( pData^[1], pData^[6]);
341 SwapBytes( pData^[2], pData^[5]);
342 SwapBytes( pData^[3], pData^[4]);
343end;
344
345
346class procedure IntegerUtils.SwapByteOrder( const pValue : Pointer; const size : Integer);
347begin
348 case size of
349 2 : Swap2( pValue);
350 4 : Swap4( pValue);
351 8 : Swap8( pValue);
352 else
353 raise EArgumentException.Create('Unexpected size');
354 end;
355end;
356
357
358{ TGuidHelper }
359
360
361function TGuidHelper.SwapByteOrder : TGuid;
362// convert to/from network byte order
363// - https://www.ietf.org/rfc/rfc4122.txt
364// - https://stackoverflow.com/questions/10850075/guid-uuid-compatibility-issue-between-net-and-linux
365// - https://lists.gnu.org/archive/html/bug-parted/2002-01/msg00099.html
366begin
367 result := Self;
368
369 IntegerUtils.SwapByteOrder( @result.D1, SizeOf(result.D1));
370 IntegerUtils.SwapByteOrder( @result.D2, SizeOf(result.D2));
371 IntegerUtils.SwapByteOrder( @result.D3, SizeOf(result.D3));
372 //result.D4 = array of byte -> implicitly correct
373end;
374
375
376{$IFDEF Debug}
377class procedure TGuidHelper.SelfTest;
378var guid : TGuid;
379 pBytes : PByteArray;
380 i, expected : Integer;
381const TEST_GUID : TGuid = '{00112233-4455-6677-8899-aabbccddeeff}';
382begin
383 // host to network
384 guid := TEST_GUID;
385 guid := guid.SwapByteOrder;
386
387 // validate network order
388 pBytes := @guid;
389 for i := 0 to $F do begin
390 expected := i * $11;
391 ASSERT( pBytes^[i] = expected);
392 end;
393
394 // network to host and final validation
395 guid := guid.SwapByteOrder;
396 ASSERT( IsEqualGuid( guid, TEST_GUID));
397end;
398{$ENDIF}
399
Jens Geyer589ee5b2021-03-29 21:40:55 +0200400
401
Jens Geyerf7904452017-07-26 15:02:12 +0200402{$IFDEF Win64}
403
404function InterlockedCompareExchange64( var Target : Int64; Exchange, Comparand : Int64) : Int64; inline;
405begin
406 {$IFDEF OLD_UNIT_NAMES}
407 result := Windows.InterlockedCompareExchange64( Target, Exchange, Comparand);
408 {$ELSE}
409 result := WinApi.Windows.InterlockedCompareExchange64( Target, Exchange, Comparand);
410 {$ENDIF}
411end;
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200412
413
Jens Geyerf7904452017-07-26 15:02:12 +0200414function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64;
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200415var old : Int64;
416begin
417 repeat
418 Old := Addend;
419 until (InterlockedCompareExchange64( Addend, Old + Value, Old) = Old);
420 result := Old;
421end;
422
Jens Geyerf7904452017-07-26 15:02:12 +0200423{$ENDIF}
Jens Geyer71070432016-01-29 10:08:39 +0100424
425
Jens Geyer8f7487e2019-05-09 22:21:32 +0200426{ EnumUtils<T> }
427
428class function EnumUtils<T>.ToString(const value : Integer) : string;
429var pType : PTypeInfo;
430begin
431 pType := PTypeInfo(TypeInfo(T));
Jens Geyer6e2a6982019-12-12 23:07:49 +0100432 if Assigned(pType)
433 and (pType^.Kind = tkEnumeration)
434 {$IF CompilerVersion >= 23.0} // TODO: Range correct? What we know is that XE does not offer it, but Rio has it
435 and (pType^.TypeData^.MaxValue >= value)
436 and (pType^.TypeData^.MinValue <= value)
437 {$ELSE}
438 and FALSE // THRIFT-5048: pType^.TypeData^ member not supported -> prevent GetEnumName() from reading outside the legal range
439 {$IFEND}
440 then result := GetEnumName( PTypeInfo(pType), value)
Jens Geyer8f7487e2019-05-09 22:21:32 +0200441 else result := IntToStr(Ord(value));
442end;
443
444
445{ StringUtils<T> }
446
447class function StringUtils<T>.ToString(const value : T) : string;
Jens Geyer85431d92019-05-10 21:17:00 +0200448type PInterface = ^IInterface;
Jens Geyer8f7487e2019-05-09 22:21:32 +0200449var pType : PTypeInfo;
Jens Geyer85431d92019-05-10 21:17:00 +0200450 stos : ISupportsToString;
451 pIntf : PInterface; // Workaround: Rio does not allow the direct typecast
Jens Geyer8f7487e2019-05-09 22:21:32 +0200452begin
453 pType := PTypeInfo(TypeInfo(T));
454 if Assigned(pType) then begin
455 case pType^.Kind of
Jens Geyerec572712020-07-13 21:15:31 +0200456
Jens Geyer8f7487e2019-05-09 22:21:32 +0200457 tkInterface : begin
Jens Geyer85431d92019-05-10 21:17:00 +0200458 pIntf := PInterface(@value);
459 if Supports( pIntf^, ISupportsToString, stos) then begin
460 result := stos.toString;
Jens Geyer8f7487e2019-05-09 22:21:32 +0200461 Exit;
462 end;
463 end;
Jens Geyerec572712020-07-13 21:15:31 +0200464
465 tkEnumeration : begin
466 case SizeOf(value) of
467 1 : begin result := EnumUtils<T>.ToString( PShortInt(@value)^); Exit; end;
468 2 : begin result := EnumUtils<T>.ToString( PSmallInt(@value)^); Exit; end;
469 4 : begin result := EnumUtils<T>.ToString( PLongInt(@value)^); Exit; end;
470 else
471 ASSERT(FALSE); // in theory, this should not happen
472 end;
473 end;
474
Jens Geyer8f7487e2019-05-09 22:21:32 +0200475 end;
476 end;
477
478 result := TValue.From<T>(value).ToString;
479end;
480
481
482{ TThriftStringBuilder }
483
484function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder;
485begin
486 Result := Append( string( RawByteString(Value)) );
487end;
488
489function TThriftStringBuilder.Append( const Value: ISupportsToString): TStringBuilder;
490begin
491 Result := Append( Value.ToString );
492end;
493
494
Jens Geyer62445c12022-06-29 00:00:00 +0200495begin
496 {$IFDEF Debug}
497 TGuid.SelfTest;
498 {$ENDIF}
Jens Geyerd5436f52014-10-03 19:50:38 +0200499end.