blob: bfd020e2a29356c0d99716a35256477aea1a45b4 [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;
83 end;
84
Jens Geyer8f7487e2019-05-09 22:21:32 +020085 EnumUtils<T> = class sealed
86 public
87 class function ToString(const value : Integer) : string; reintroduce; static; inline;
88 end;
89
90 StringUtils<T> = class sealed
91 public
92 class function ToString(const value : T) : string; reintroduce; static; inline;
93 end;
94
Jens Geyer71070432016-01-29 10:08:39 +010095
Jens Geyer83ff7532019-06-06 22:46:03 +020096const
97 THRIFT_MIMETYPE = 'application/x-thrift';
98
Jens Geyerf7904452017-07-26 15:02:12 +020099{$IFDEF Win64}
Jens Geyer6e2a6982019-12-12 23:07:49 +0100100function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64;
Jens Geyerf7904452017-07-26 15:02:12 +0200101{$ENDIF}
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200102
Jens Geyer71070432016-01-29 10:08:39 +0100103
Jens Geyerd5436f52014-10-03 19:50:38 +0200104implementation
105
Jens Geyerd5436f52014-10-03 19:50:38 +0200106{ TOverlappedHelperImpl }
107
108constructor TOverlappedHelperImpl.Create;
109begin
110 inherited Create;
111 FillChar( FOverlapped, SizeOf(FOverlapped), 0);
112 FEvent := TEvent.Create( nil, TRUE, FALSE, ''); // always ManualReset, see MSDN
113 FOverlapped.hEvent := FEvent.Handle;
114end;
115
116
117
118destructor TOverlappedHelperImpl.Destroy;
119begin
120 try
121 FOverlapped.hEvent := 0;
122 FreeAndNil( FEvent);
123
124 finally
125 inherited Destroy;
126 end;
127
128end;
129
130
131function TOverlappedHelperImpl.Overlapped : TOverlapped;
132begin
133 result := FOverlapped;
134end;
135
136
137function TOverlappedHelperImpl.OverlappedPtr : POverlapped;
138begin
139 result := @FOverlapped;
140end;
141
142
143function TOverlappedHelperImpl.WaitHandle : THandle;
144begin
145 result := FOverlapped.hEvent;
146end;
147
148
149function TOverlappedHelperImpl.WaitFor( dwTimeout : DWORD) : DWORD;
150begin
151 result := WaitForSingleObject( FOverlapped.hEvent, dwTimeout);
152end;
153
154
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100155{ Base64Utils }
156
157class function Base64Utils.Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
158const ENCODE_TABLE : PAnsiChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
159begin
160 ASSERT( len in [1..3]);
161 dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shr 2) and $3F]);
162 case len of
163 3 : begin
164 Inc(dstOff);
165 dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
166 Inc(dstOff);
167 dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff + 1] shl 2) and $3C) or ((src[srcOff + 2] shr 6) and $03)]);
168 Inc(dstOff);
169 dst[dstOff] := Byte( ENCODE_TABLE[ src[srcOff + 2] and $3F]);
170 result := 4;
171 end;
172
173 2 : begin
174 Inc(dstOff);
175 dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
176 Inc(dstOff);
177 dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff + 1] shl 2) and $3C]);
178 result := 3;
179 end;
180
181 1 : begin
182 Inc(dstOff);
183 dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shl 4) and $30]);
184 result := 2;
185 end;
186
187 else
188 ASSERT( FALSE);
Jens Geyer9f9535c2014-12-14 04:16:05 +0100189 result := 0; // because invalid call
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100190 end;
191end;
192
193
194class function Base64Utils.Decode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
195const DECODE_TABLE : array[0..$FF] of Integer
196 = ( -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
197 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
198 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
199 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
200 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
201 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
202 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
203 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
204 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
205 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-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 );
212begin
213 ASSERT( len in [1..4]);
214 result := 1;
215 dst[dstOff] := ((DECODE_TABLE[src[srcOff] and $0FF] shl 2)
216 or (DECODE_TABLE[src[srcOff + 1] and $0FF] shr 4));
217
218 if (len > 2) then begin
219 Inc( result);
220 Inc( dstOff);
221 dst[dstOff] := (((DECODE_TABLE[src[srcOff + 1] and $0FF] shl 4) and $F0)
222 or (DECODE_TABLE[src[srcOff + 2] and $0FF] shr 2));
223
224 if (len > 3) then begin
225 Inc( result);
226 Inc( dstOff);
227 dst[dstOff] := (((DECODE_TABLE[src[srcOff + 2] and $0FF] shl 6) and $C0)
228 or DECODE_TABLE[src[srcOff + 3] and $0FF]);
229 end;
230 end;
231end;
Jens Geyerd5436f52014-10-03 19:50:38 +0200232
233
Jens Geyer71070432016-01-29 10:08:39 +0100234class function CharUtils.IsHighSurrogate( const c : Char) : Boolean;
235begin
Jens Geyer36c0b342018-01-19 19:17:33 +0100236 {$IF CompilerVersion < 25.0}
237 {$IFDEF OLD_UNIT_NAMES}
238 result := Character.IsHighSurrogate(c);
239 {$ELSE}
240 result := System.Character.IsHighSurrogate(c);
241 {$ENDIF}
Jens Geyer71070432016-01-29 10:08:39 +0100242 {$ELSE}
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200243 result := c.IsHighSurrogate();
Jens Geyer71070432016-01-29 10:08:39 +0100244 {$IFEND}
245end;
246
247
248class function CharUtils.IsLowSurrogate( const c : Char) : Boolean;
249begin
Jens Geyer36c0b342018-01-19 19:17:33 +0100250 {$IF CompilerVersion < 25.0}
251 {$IFDEF OLD_UNIT_NAMES}
252 result := Character.IsLowSurrogate(c);
253 {$ELSE}
254 result := System.Character.IsLowSurrogate(c);
255 {$ENDIF}
Jens Geyer71070432016-01-29 10:08:39 +0100256 {$ELSE}
Jens Geyer36c0b342018-01-19 19:17:33 +0100257 result := c.IsLowSurrogate();
Jens Geyer71070432016-01-29 10:08:39 +0100258 {$IFEND}
259end;
260
261
Jens Geyerf7904452017-07-26 15:02:12 +0200262{$IFDEF Win64}
263
264function InterlockedCompareExchange64( var Target : Int64; Exchange, Comparand : Int64) : Int64; inline;
265begin
266 {$IFDEF OLD_UNIT_NAMES}
267 result := Windows.InterlockedCompareExchange64( Target, Exchange, Comparand);
268 {$ELSE}
269 result := WinApi.Windows.InterlockedCompareExchange64( Target, Exchange, Comparand);
270 {$ENDIF}
271end;
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200272
273
Jens Geyerf7904452017-07-26 15:02:12 +0200274function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64;
Jens Geyer9f7f11e2016-04-14 21:37:11 +0200275var old : Int64;
276begin
277 repeat
278 Old := Addend;
279 until (InterlockedCompareExchange64( Addend, Old + Value, Old) = Old);
280 result := Old;
281end;
282
Jens Geyerf7904452017-07-26 15:02:12 +0200283{$ENDIF}
Jens Geyer71070432016-01-29 10:08:39 +0100284
285
Jens Geyer8f7487e2019-05-09 22:21:32 +0200286{ EnumUtils<T> }
287
288class function EnumUtils<T>.ToString(const value : Integer) : string;
289var pType : PTypeInfo;
290begin
291 pType := PTypeInfo(TypeInfo(T));
Jens Geyer6e2a6982019-12-12 23:07:49 +0100292 if Assigned(pType)
293 and (pType^.Kind = tkEnumeration)
294 {$IF CompilerVersion >= 23.0} // TODO: Range correct? What we know is that XE does not offer it, but Rio has it
295 and (pType^.TypeData^.MaxValue >= value)
296 and (pType^.TypeData^.MinValue <= value)
297 {$ELSE}
298 and FALSE // THRIFT-5048: pType^.TypeData^ member not supported -> prevent GetEnumName() from reading outside the legal range
299 {$IFEND}
300 then result := GetEnumName( PTypeInfo(pType), value)
Jens Geyer8f7487e2019-05-09 22:21:32 +0200301 else result := IntToStr(Ord(value));
302end;
303
304
305{ StringUtils<T> }
306
307class function StringUtils<T>.ToString(const value : T) : string;
Jens Geyer85431d92019-05-10 21:17:00 +0200308type PInterface = ^IInterface;
Jens Geyer8f7487e2019-05-09 22:21:32 +0200309var pType : PTypeInfo;
Jens Geyer85431d92019-05-10 21:17:00 +0200310 stos : ISupportsToString;
311 pIntf : PInterface; // Workaround: Rio does not allow the direct typecast
Jens Geyer8f7487e2019-05-09 22:21:32 +0200312begin
313 pType := PTypeInfo(TypeInfo(T));
314 if Assigned(pType) then begin
315 case pType^.Kind of
Jens Geyerec572712020-07-13 21:15:31 +0200316
Jens Geyer8f7487e2019-05-09 22:21:32 +0200317 tkInterface : begin
Jens Geyer85431d92019-05-10 21:17:00 +0200318 pIntf := PInterface(@value);
319 if Supports( pIntf^, ISupportsToString, stos) then begin
320 result := stos.toString;
Jens Geyer8f7487e2019-05-09 22:21:32 +0200321 Exit;
322 end;
323 end;
Jens Geyerec572712020-07-13 21:15:31 +0200324
325 tkEnumeration : begin
326 case SizeOf(value) of
327 1 : begin result := EnumUtils<T>.ToString( PShortInt(@value)^); Exit; end;
328 2 : begin result := EnumUtils<T>.ToString( PSmallInt(@value)^); Exit; end;
329 4 : begin result := EnumUtils<T>.ToString( PLongInt(@value)^); Exit; end;
330 else
331 ASSERT(FALSE); // in theory, this should not happen
332 end;
333 end;
334
Jens Geyer8f7487e2019-05-09 22:21:32 +0200335 end;
336 end;
337
338 result := TValue.From<T>(value).ToString;
339end;
340
341
342{ TThriftStringBuilder }
343
344function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder;
345begin
346 Result := Append( string( RawByteString(Value)) );
347end;
348
349function TThriftStringBuilder.Append( const Value: ISupportsToString): TStringBuilder;
350begin
351 Result := Append( Value.ToString );
352end;
353
354
Jens Geyerd5436f52014-10-03 19:50:38 +0200355end.