33(* ******************************************************************************
44* Author : Angus Johnson *
55* Version : 4.9 *
6- * Date : 15 December 2025 *
6+ * Date : 8 April 2026 *
77* Website : https://www.angusj.com *
8- * Copyright : Angus Johnson 2019-2025 *
8+ * Copyright : Angus Johnson 2019-2026 *
99* *
1010* Purpose : Essential structures and functions to read SVG files *
1111* *
@@ -244,9 +244,14 @@ TSvgParser = class
244244 skipComma: Boolean; out val: double): Boolean;
245245 function ParseNextNumEx (var c: PUTF8Char; endC: PUTF8Char; skipComma: Boolean;
246246 out val: double; out unitType: TUnitType): Boolean;
247- function GetHash (c: PUTF8Char; len: nativeint): cardinal; overload;
248- function GetHash (const name : UTF8String): cardinal; overload; { $IFDEF INLINE} inline; { $ENDIF}
249- function GetHashCaseSensitive (name : PUTF8Char; nameLen: integer): cardinal;
247+
248+ function GetHashCaseSensitive (c: PUTF8Char; len: nativeint): cardinal; overload;
249+ function GetHashCaseInsensitive (c: PUTF8Char; len: nativeint): cardinal; overload;
250+ function GetHashCaseSensitive (const name : UTF8String): cardinal; overload; { $IFDEF INLINE} inline; { $ENDIF}
251+ function GetHashCaseInsensitive (const name : UTF8String): cardinal; overload; { $IFDEF INLINE} inline; { $ENDIF}
252+
253+ // function GetHashCaseSensitive(name: PUTF8Char; nameLen: integer): cardinal; overload;
254+ // function GetHashCaseInsensitive(name: PUTF8Char; nameLen: integer): cardinal; overload;
250255 function ExtractRef (const href: UTF8String): UTF8String;
251256 function IsNumPending (var c: PUTF8Char;
252257 endC: PUTF8Char; ignoreComma: Boolean): Boolean;
@@ -295,6 +300,7 @@ TSvgParser = class
295300 TSetOfUTF8Char = set of UTF8Char;
296301
297302function CharInSet (chr: UTF8Char; const chrs: TSetOfUTF8Char): Boolean;
303+ { $IFDEF INLINE} inline; { $ENDIF}
298304function DecodeUtf8ToUnicode (const utf8: UTF8String): UnicodeString;
299305
300306const
@@ -599,7 +605,8 @@ function Match(c: PUTF8Char; const compare: UTF8String): Boolean;
599605 Result := false;
600606 for i := 1 to Length(compare) do
601607 begin
602- if LowerCaseTable[c[i - 1 ]] <> compare[i] then Exit;
608+ // if LowerCaseTable[c[i - 1]] <> compare[i] then Exit;
609+ if c[i - 1 ] <> compare[i] then Exit;
603610 end ;
604611 Result := true;
605612end ;
@@ -616,7 +623,8 @@ function Match(const compare1, compare2: UTF8String): Boolean;
616623 c1 := @compare1[1 ]; c2 := @compare2[1 ];
617624 for i := 1 to len do
618625 begin
619- if LowerCaseTable[c1[i - 1 ]] <> LowerCaseTable[c2[i - 1 ]] then Exit;
626+ // if LowerCaseTable[c1[i - 1]] <> LowerCaseTable[c2[i - 1]] then Exit;
627+ if c1[i - 1 ] <> c2[i - 1 ] then Exit;
620628 end ;
621629 Result := true;
622630end ;
@@ -888,7 +896,7 @@ function ParseNextWordHash(var c: PUTF8Char; endC: PUTF8Char; out hash: cardinal
888896 end ;
889897 end ;
890898 c := cc;
891- hash := GetHash (c2, cc - c2);
899+ hash := GetHashCaseSensitive (c2, cc - c2);
892900 Result := True;
893901end ;
894902// ------------------------------------------------------------------------------
@@ -912,7 +920,7 @@ function ParseNextWordHash(c, endC: PUTF8Char): cardinal;
912920 else break;
913921 end ;
914922 end ;
915- Result := GetHash (c2, c - c2);
923+ Result := GetHashCaseInsensitive (c2, c - c2);
916924end ;
917925// ------------------------------------------------------------------------------
918926
@@ -935,7 +943,7 @@ function ParseNextWordExHash(var c: PUTF8Char; endC: PUTF8Char;
935943 inc(c);
936944 c2 := cc;
937945 while (cc < endC) and (cc^ <> quote) do inc(cc);
938- hash := GetHash (c2, cc - c2);
946+ hash := GetHashCaseSensitive (c2, cc - c2);
939947 inc(cc);
940948 end else
941949 begin
@@ -952,7 +960,7 @@ function ParseNextWordExHash(var c: PUTF8Char; endC: PUTF8Char;
952960 ' A' ..' Z' , ' a' ..' z' , ' -' , ' _' : inc(cc);
953961 else break;
954962 end ;
955- hash := GetHash (c2, cc - c2);
963+ hash := GetHashCaseSensitive (c2, cc - c2);
956964 end ;
957965 c := cc;
958966 Result := True;
@@ -974,7 +982,7 @@ function ParseNameLength(c: PUTF8Char; endC: PUTF8Char): PUTF8Char;
974982// ------------------------------------------------------------------------------
975983
976984{ $OVERFLOWCHECKS OFF}
977- function GetHash (c: PUTF8Char; len: nativeint): cardinal;
985+ function GetHashCaseSensitive (c: PUTF8Char; len: nativeint): cardinal;
978986var
979987 i: integer;
980988begin
@@ -983,7 +991,8 @@ function GetHash(c: PUTF8Char; len: nativeint): cardinal;
983991 if c = nil then Exit;
984992 for i := 1 to len do
985993 begin
986- Result := (Result + Ord(LowerCaseTable[c^]));
994+ // Result := (Result + Ord(LowerCaseTable[c^]));
995+ Result := Result + Ord(c^);
987996 Result := Result + (Result shl 10 );
988997 Result := Result xor (Result shr 6 );
989998 inc(c);
@@ -992,40 +1001,46 @@ function GetHash(c: PUTF8Char; len: nativeint): cardinal;
9921001 Result := Result xor (Result shr 11 );
9931002 Result := Result + (Result shl 15 );
9941003end ;
995- { $IFDEF OVERFLOWCHECKS_ENABLED}
996- { $OVERFLOWCHECKS ON}
997- { $ENDIF}
9981004// ------------------------------------------------------------------------------
9991005
1000- function GetHash (const name : UTF8String): cardinal;
1001- begin
1002- // skip function call by directly casting it to Pointer
1003- Result := GetHash(PUTF8Char(Pointer(name )), Length(name ));
1004- end ;
1005- // ------------------------------------------------------------------------------
1006-
1007- { $OVERFLOWCHECKS OFF}
1008- function GetHashCaseSensitive (name : PUTF8Char; nameLen: integer): cardinal;
1006+ function GetHashCaseInsensitive (c: PUTF8Char; len: nativeint): cardinal;
10091007var
10101008 i: integer;
10111009begin
1010+ // https://en.wikipedia.org/wiki/Jenkins_hash_function
10121011 Result := 0 ;
1013- for i := 1 to nameLen do
1012+ if c = nil then Exit;
1013+ for i := 1 to len do
10141014 begin
1015- Result := (Result + Ord(name ^ ));
1015+ Result := (Result + Ord(LowerCaseTable[c^] ));
10161016 Result := Result + (Result shl 10 );
10171017 Result := Result xor (Result shr 6 );
1018- inc(name );
1018+ inc(c );
10191019 end ;
10201020 Result := Result + (Result shl 3 );
10211021 Result := Result xor (Result shr 11 );
10221022 Result := Result + (Result shl 15 );
10231023end ;
1024+
10241025{ $IFDEF OVERFLOWCHECKS_ENABLED}
10251026 { $OVERFLOWCHECKS ON}
10261027{ $ENDIF}
10271028// ------------------------------------------------------------------------------
10281029
1030+ function GetHashCaseSensitive (const name : UTF8String): cardinal;
1031+ begin
1032+ // skip function call by directly casting it to Pointer
1033+ Result := GetHashCaseSensitive(PUTF8Char(Pointer(name )), Length(name ));
1034+ end ;
1035+ // ------------------------------------------------------------------------------
1036+
1037+ function GetHashCaseInsensitive (const name : UTF8String): cardinal;
1038+ begin
1039+ // skip function call by directly casting it to Pointer
1040+ Result := GetHashCaseInsensitive(PUTF8Char(Pointer(name )), Length(name ));
1041+ end ;
1042+ // ------------------------------------------------------------------------------
1043+
10291044function ParseNextWordHashed (var c: PUTF8Char; endC: PUTF8Char): cardinal;
10301045var
10311046 c2: PUTF8Char;
@@ -1035,7 +1050,7 @@ function ParseNextWordHashed(var c: PUTF8Char; endC: PUTF8Char): cardinal;
10351050 c := ParseNameLength(c2, endC);
10361051 len := c - c2;
10371052 if len <= 0 then Result := 0
1038- else Result := GetHash (c2, len);
1053+ else Result := GetHashCaseInsensitive (c2, len);
10391054end ;
10401055// ------------------------------------------------------------------------------
10411056
@@ -1555,7 +1570,7 @@ function IsKnownEntity(owner: TSvgParser;
15551570 inc(c); // skip ampersand.
15561571 c2 := c; c3 := c;
15571572 c3 := ParseNameLength(c3, endC);
1558- entity := owner.FindEntity(GetHash (c2, c3 - c2));
1573+ entity := owner.FindEntity(GetHashCaseInsensitive (c2, c3 - c2));
15591574 Result := (c3^ = ' ;' ) and Assigned(entity);
15601575 // nb: increments 'c' only if the entity is found.
15611576 if Result then c := c3 +1 else dec(c);
@@ -2168,7 +2183,7 @@ class function TXmlEl.ParseAttribName(c, endC: PUTF8Char; attrib: PSvgAttrib): P
21682183 Delete(attrib.name , 1 , i);
21692184 end ;
21702185
2171- attrib.hash := GetHash (attrib.Name );
2186+ attrib.hash := GetHashCaseInsensitive (attrib.Name );
21722187end ;
21732188// ------------------------------------------------------------------------------
21742189
@@ -2346,7 +2361,7 @@ procedure TXmlEl.ParseStyleAttribute(const style: UTF8String);
23462361 attrib := NewSvgAttrib();
23472362 attrib.name := styleName;
23482363 attrib.value := styleVal;
2349- attrib.hash := GetHash (attrib.name );
2364+ attrib.hash := GetHashCaseInsensitive (attrib.name );
23502365 attribs.Add(attrib);
23512366 end ;
23522367end ;
@@ -2484,7 +2499,7 @@ function TXmlEl.ParseContent(var c: PUTF8Char; endC: PUTF8Char): Boolean;
24842499 while (cc < endC) and (cc^ <> ' <' ) do inc(cc);
24852500 child := TSvgXmlEl.Create(owner);
24862501 child.name := ' tspan' ;
2487- child.hash := GetHash (' tspan' );
2502+ child.hash := GetHashCaseInsensitive (' tspan' );
24882503 child.selfClosed := true; // //////////////////// :)))
24892504 childs.Add(child);
24902505 ToUTF8String(c, cc, child.text, sitPreserve);
@@ -2602,7 +2617,7 @@ procedure TSvgXmlEl.Clear;
26022617function TSvgXmlEl.ParseHeader (var c: PUTF8Char; endC: PUTF8Char): Boolean;
26032618begin
26042619 Result := inherited ParseHeader(c, endC);
2605- if Result then hash := GetHash (name );
2620+ if Result then hash := GetHashCaseInsensitive (name );
26062621end ;
26072622// ------------------------------------------------------------------------------
26082623
@@ -3200,7 +3215,7 @@ procedure TClassStylesList.Preallocate(AdditionalItemCount: Integer);
32003215function TClassStylesList.FindItemIndex (const Name : UTF8String): Integer;
32013216begin
32023217 Result := -1 ;
3203- FNameHash := GetHash (Name );
3218+ FNameHash := GetHashCaseInsensitive (Name );
32043219 if FMod <> 0 then
32053220 begin
32063221 Result := FBuckets[(FNameHash and $7FFFFFFF) mod FMod];
@@ -3294,7 +3309,7 @@ constructor TColorConstList.Create(Colors: PColorConst; Count: Integer);
32943309 Item := @FItems[I];
32953310 Item.Data := Colors; // link the constant to the ColorConstMapItem
32963311 Inc(Colors);
3297- Item.Hash := GetHash (Item.Data.ColorName); // case-insensitive
3312+ Item.Hash := GetHashCaseInsensitive (Item.Data.ColorName);
32983313 Bucket := @FBuckets[(Cardinal(Item.Hash) and $7FFFFFFF) mod FMod];
32993314 Item.Next := Bucket^;
33003315 Bucket^ := Item;
@@ -3307,7 +3322,7 @@ function TColorConstList.GetColorValue(const ColorName: UTF8String; var Color: T
33073322 Hash: Cardinal;
33083323 Item: PColorConstMapItem;
33093324begin
3310- Hash := GetHash (ColorName);
3325+ Hash := GetHashCaseInsensitive (ColorName);
33113326 Item := FBuckets[(Cardinal(Hash) and $7FFFFFFF) mod FMod];
33123327 while (Item <> nil ) and
33133328 not IsSameAsciiUTF8String(Item.Data.ColorName, ColorName) do
0 commit comments