github.com/remobjects/goldbaselibrary@v0.0.0-20230924164425-d458680a936b/Source/Gold/string.pas (about) 1 namespace go.builtin; 2 3 uses 4 {$IFDEF ECHOES} System.Text, System.Linq, System.Collections.Generic{$ENDIF} 5 ; 6 7 type 8 string = public partial record 9 public 10 Value: Slice<byte>; 11 12 constructor(aValue: PlatformString); 13 begin 14 {$IF ISLAND} 15 Value := Encoding.UTF8.GetBytes(aValue); 16 {$ELSEIF ECHOES} 17 Value := System.Text.Encoding.UTF8.GetBytes(aValue); 18 {$ENDIF} 19 end; 20 21 constructor; 22 begin 23 Value := new Slice<byte>(0); 24 end; 25 26 constructor(aValue: array of Char); 27 begin 28 {$IF ECHOES} 29 Value := Encoding.UTF8.GetBytes(aValue); 30 {$ELSE} 31 var lString := PlatformString.FromCharArray(aValue); 32 Value := Encoding.UTF8.GetBytes(lString); 33 {$ENDIF} 34 end; 35 36 constructor(aValue: Slice<byte>); 37 begin 38 Value := new Slice<byte>(aValue.Length); 39 copy(Value, aValue); 40 end; 41 42 constructor(aValue: array of rune); 43 begin 44 var lArray := new Char[aValue.Length]; 45 for i: Integer := 0 to aValue.Length - 1 do 46 lArray[i] := chr(aValue[i]); 47 48 constructor(lArray); 49 end; 50 51 class operator Implicit(aValue: string): PlatformString; 52 begin 53 {$IF ISLAND} 54 result := Encoding.UTF8.GetString(aValue.Value); 55 {$ELSEIF ECHOES} 56 result := System.Text.Encoding.UTF8.GetString(aValue.Value); 57 {$ENDIF} 58 end; 59 60 class operator Implicit(aValue: PlatformString): string; 61 begin 62 {$IF ISLAND} 63 result := new string(Encoding.UTF8.GetBytes(aValue)); 64 {$ELSEIF ECHOES} 65 result := new string(System.Text.Encoding.UTF8.GetBytes(aValue)); 66 {$ENDIF} 67 end; 68 69 class operator Implicit(aValue: array of uint8): string; 70 begin 71 result := new string(aValue); 72 end; 73 74 class operator Implicit(aValue: string): Slice<byte>; 75 begin 76 if aValue.Value ≠ nil then begin 77 var lData := aValue.Value.ToArray; 78 result := new Slice<byte>(lData); 79 end 80 else 81 result := new Slice<byte>(0); 82 end; 83 84 class operator Implicit(aValue: Char): string; 85 begin 86 result := new string([aValue]); 87 end; 88 89 class operator Equal(a, b: string): Boolean; 90 begin 91 result := go.bytes.Compare(a.Value, b.Value) = 0; 92 end; 93 94 class operator NotEqual(a, b: string): Boolean; 95 begin 96 result := not (a = b); 97 end; 98 99 class operator Less(aLeft, aRight: string): Boolean; 100 begin 101 result := go.bytes.Compare(aLeft.Value, aRight.Value) < 0; 102 end; 103 104 class operator LessOrEqual(aLeft, aRight: string): Boolean; 105 begin 106 result := go.bytes.Compare(aLeft.Value, aRight.Value) <= 0; 107 end; 108 109 class operator GreaterOrEqual(aLeft, aRight: string): Boolean; 110 begin 111 result := go.bytes.Compare(aLeft.Value, aRight.Value) ≥ 0; 112 end; 113 114 class operator Greater(aLeft, aRight: string): Boolean; 115 begin 116 result := go.bytes.Compare(aLeft, aRight) > 0; 117 end; 118 119 class operator &Add(aLeft, aRight: string): string; 120 begin 121 var lNew := new byte[aLeft.Length]; 122 for i: Integer := 0 to aLeft.Length - 1 do 123 lNew[i] := aLeft[i]; 124 result := new string(append(aLeft, aRight.Value)); 125 end; 126 127 class operator &Add(aLeft: string; aRight: Integer): string; 128 begin 129 result := aLeft + go.strconv.Itoa(aRight); 130 end; 131 132 // go does not have Substring method 133 method Substring(aIndex: int32): string; 134 begin 135 result := new string(new Slice<byte>(Value, aIndex, Value.Length - 1)); 136 end; 137 138 method Substring(aIndex: int32; aLen: int32): string; 139 begin 140 var lLength := (aIndex + aLen) - 1; 141 if lLength < 0 then 142 lLength := 0; 143 result := new string(new Slice<byte>(Value, aIndex, lLength)); 144 end; 145 146 class method IsNullOrEmpty(aValue: string): Boolean; public; 147 begin 148 result := (aValue.Value = nil) or (aValue.Value.Length = 0); 149 end; 150 151 property Chars[aIndex: int32]: byte read begin 152 result := Value[aIndex]; 153 end; default; inline; 154 155 property Length: Integer read if Value ≠ nil then Value.Length else 0; 156 157 method GetSequence: sequence of tuple of (Integer, rune); iterator; 158 begin 159 var i := 0; 160 var c := 0; 161 var r: rune; 162 while i < Value.Length do begin 163 r := UTF8ToString(Value, i, var c); 164 yield (i, r); 165 i := i + c; 166 end; 167 end; 168 169 method Name: string; 170 begin 171 result := ''; 172 end; 173 174 method ToString: PlatformString; override; 175 begin 176 {$IF ISLAND} 177 result := Encoding.UTF8.GetString(Value); 178 {$ELSEIF ECHOES} 179 result := System.Text.Encoding.UTF8.GetString(Value); 180 {$ENDIF} 181 end; 182 183 method GetHashCode: Integer; override; public; 184 begin 185 {$IF ISLAND} 186 if (Value.fArray ≠ nil) and (Value.fArray.Length > 0) then 187 result := Utilities.CalcHash(^Void(@Value.fArray[0]), Value.Length) 188 else 189 result := 0; 190 {$ELSEIF ECHOES} 191 result := System.Collections.IStructuralEquatable(Value).GetHashCode(EqualityComparer<byte>.Default); 192 {$ENDIF} 193 end; 194 195 method &Equals(obj: Object): Boolean; override; public; 196 begin 197 var lOther := string(obj); 198 if assigned(lOther) then 199 result := lOther = self; 200 end; 201 202 class var fZero: string := new string(); 203 class property Zero: string := fZero; published; 204 205 class method UTF8ToString(aValue: array of byte; aIndex: Integer; var aSize: Integer): rune; 206 begin 207 if aValue = nil then new ArgumentNullException('aValue is nil'); 208 var len := aValue.Length; 209 if len = 0 then exit 0; 210 var pos := aIndex; 211 var last := aValue.Length; 212 // skip BOM 213 if len>2 then begin 214 if (aValue[0] = $EF) and 215 (aValue[1] = $BB) and 216 (aValue[2] = $BF) then pos := 3; 217 end; 218 var ch := aValue[pos]; 219 if ch and $F0 = $F0 then begin 220 // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 221 if pos+4 > last then 222 raise new Exception('Bad UTF8 string'); 223 var code := ((((( 224 uint32(ch and $7) shl 6 + 225 (uint32(aValue[pos+1]) and $3F)) shl 6)+ 226 (uint32(aValue[pos+2]) and $3F)) shl 6)+ 227 (uint32(aValue[pos+3]) and $3F)); 228 //if (code < $10000) or (code>$1FFFFF) then MalformedError; 229 var code1 := code - $10000; 230 //str.Append(Char($D800 or (code1 shr 10))); 231 //str.Append(Char($DC00 or (code1 and $3FF))); 232 aSize := 4; 233 exit 65; // TODO 234 end 235 else if (ch and $E0) = $E0 then begin 236 //1110xxxx 10xxxxxx 10xxxxxx 237 if pos+3 > last then 238 raise new Exception('Bad UTF8 string'); 239 var code := (( 240 uint32(ch and $F) shl 6 + 241 (uint32(aValue[pos+1]) and $3F)) shl 6)+ 242 (uint32(aValue[pos+2]) and $3F); 243 if (code < $800) or (code > $FFFF) then 244 raise new Exception('Bad UTF8 string'); 245 aSize := 3; 246 exit code; 247 end 248 else if (ch and $C0) = $C0 then begin 249 // 110xxxxx 10xxxxxx 250 if pos+2 > last then 251 raise new Exception('Bad UTF8 string'); 252 var code := 253 uint32(ch and $1F) shl 6 + 254 (uint32(aValue[pos+1]) and $3F); 255 if (code < $80) or (code >$7FF) then 256 raise new Exception('Bad UTF8 string'); 257 aSize := 2; 258 exit code; 259 end 260 else begin 261 // 0xxxxxxx 262 var code := ch; 263 if (code < $0) or (code > $7F) then 264 raise new Exception('Bad UTF8 string'); 265 aSize := 1; 266 exit code; 267 end; 268 end; 269 270 class method PlatformStringArrayToGoArray(aValue: array of PlatformString): array of go.builtin.string; 271 begin 272 result := new go.builtin.string[aValue.Length]; 273 for i: Integer := 0 to aValue.Length - 1 do 274 result[i] := aValue[i]; 275 end; 276 277 class method PlatformStringArrayToGoSlice(aValue: array of PlatformString): Slice<go.builtin.string>; 278 begin 279 result := new Slice<go.builtin.string>(aValue.Length); 280 for i: Integer := 0 to aValue.Length - 1 do 281 result[i] := aValue[i]; 282 end; 283 end; 284 285 operator implicit(aVal: string): Slice<Char>; public; 286 begin 287 var lString := Encoding.UTF8.GetString(aVal.Value); 288 result := new Slice<Char>(lString.ToCharArray); 289 end; 290 291 operator implicit(aVal: string): Slice<go.builtin.rune>; public; 292 begin 293 var lString := Encoding.UTF8.GetString(aVal.Value); 294 result := new Slice<rune>(lString.Select(a -> rune(a)).ToArray()); 295 end; 296 297 operator implicit(aVal: byte): string; public; 298 begin 299 exit new string([rune(aVal)]); 300 end; 301 302 operator implicit(aVal: rune): string; public; 303 begin 304 exit new string([aVal]); 305 end; 306 307 operator Implicit(aVal: Slice<Char>): string; public; 308 begin 309 exit new string(aVal.ToArray()); 310 end; 311 312 operator Implicit(aVal: Slice<rune>): string; public; 313 begin 314 exit new string(aVal.ToArray()); 315 end; 316 317 operator Explicit(aVal: string): Slice<byte>; public; 318 begin 319 result := new Slice<byte>(aVal.Length); 320 copy(result, aVal.Value); 321 end; 322 323 operator Explicit(aVal: PlatformString): Slice<byte>; public; 324 begin 325 {$IFDEF ISLAND} 326 exit new Slice<byte>(Encoding.UTF8.GetBytes(aVal)); 327 {$ELSE} 328 exit new Slice<byte>(System.Text.Encoding.UTF8.GetBytes(aVal)); 329 {$ENDIF} 330 end; 331 332 operator Explicit(aVal: PlatformString): go.net.http.htmlSig; public; 333 begin 334 var q: go.builtin.Slice<byte> := (aVal as go.builtin.Slice<byte>); 335 {$IF ISLAND} 336 exit new go.net.http.htmlSig(Value := Encoding.UTF8.GetBytes(aVal)); 337 result := nil; 338 {$ELSEIF ECHOES} 339 exit new go.net.http.htmlSig(Value := System.Text.Encoding.UTF8.GetBytes(aVal)); 340 {$ENDIF} 341 end; 342 343 operator Implicit(aVal: Slice<byte>): string; public; 344 begin 345 exit new string(aVal); 346 end; 347 348 operator Implicit(aVal: PlatformString): Slice<byte>; public; 349 begin 350 {$IF ISLAND} 351 result := new Slice<byte>(Encoding.UTF8.GetBytes(aVal)); 352 {$ELSEIF ECHOES} 353 result := new Slice<byte>(System.Text.Encoding.UTF8.GetBytes(aVal)); 354 {$ENDIF} 355 end; 356 357 operator Implicit(aVal: PlatformString): array of byte; public; 358 begin 359 {$IF ISLAND} 360 result := Encoding.UTF8.GetBytes(aVal); 361 {$ELSEIF ECHOES} 362 result := System.Text.Encoding.UTF8.GetBytes(aVal); 363 {$ENDIF} 364 end; 365 366 operator Implicit(aVal: Slice<byte>): PlatformString; public; 367 begin 368 {$IF ISLAND} 369 result := Encoding.UTF8.GetString(aVal); 370 {$ELSEIF ECHOES} 371 result := System.Text.Encoding.UTF8.GetString(aVal); 372 {$ENDIF} 373 end; 374 375 operator implicit(aVal: PlatformString): Slice<go.builtin.rune>; public; 376 begin 377 if aVal ≠ nil then begin 378 var lChars := aVal.ToCharArray; 379 result := new go.builtin.Slice<go.builtin.rune>(lChars.Select(a -> go.builtin.rune(a)).ToArray()); 380 end 381 else 382 result := new go.builtin.Slice<go.builtin.rune>(0); 383 end; 384 385 end.