@@ -32,32 +32,29 @@ + (instancetype)segmentWithLatex:(NSString *)latex
3232@end
3333
3434@implementation ENRMRenderedSegment
35- + (instancetype )textSegmentWithResult : (ENRMRenderResult *)result signature : (NSString * )signature
35+ + (instancetype )textSegmentWithResult : (ENRMRenderResult *)result signature : (uint64_t )signature
3636{
3737 NSParameterAssert (result != nil );
38- NSParameterAssert (signature != nil );
3938 ENRMRenderedSegment *segment = [[ENRMRenderedSegment alloc ] init ];
4039 segment.kind = ENRMSegmentKindText;
4140 segment.textResult = result;
4241 segment.signature = signature;
4342 return segment;
4443}
4544
46- + (instancetype )tableSegmentWithSegment : (ENRMTableSegment *)tableSegment signature : (NSString * )signature
45+ + (instancetype )tableSegmentWithSegment : (ENRMTableSegment *)tableSegment signature : (uint64_t )signature
4746{
4847 NSParameterAssert (tableSegment != nil );
49- NSParameterAssert (signature != nil );
5048 ENRMRenderedSegment *segment = [[ENRMRenderedSegment alloc ] init ];
5149 segment.kind = ENRMSegmentKindTable;
5250 segment.tableSegment = tableSegment;
5351 segment.signature = signature;
5452 return segment;
5553}
5654
57- + (instancetype )mathSegmentWithSegment : (ENRMMathSegment *)mathSegment signature : (NSString * )signature
55+ + (instancetype )mathSegmentWithSegment : (ENRMMathSegment *)mathSegment signature : (uint64_t )signature
5856{
5957 NSParameterAssert (mathSegment != nil );
60- NSParameterAssert (signature != nil );
6158 ENRMRenderedSegment *segment = [[ENRMRenderedSegment alloc ] init ];
6259 segment.kind = ENRMSegmentKindMath;
6360 segment.mathSegment = mathSegment;
@@ -66,31 +63,68 @@ + (instancetype)mathSegmentWithSegment:(ENRMMathSegment *)mathSegment signature:
6663}
6764@end
6865
69- NSString *ENRMSignatureForNode (MarkdownASTNode *node)
66+ // FNV-1a 64-bit hash for segment signatures. Collisions are theoretically
67+ // possible but negligible for the small number of segments per document
68+ // (~single digits). Worst case is a single skipped view update, corrected
69+ // on the next streaming tick. This replaces the previous approach of building
70+ // and comparing multi-KB NSString signatures from the full AST subtree.
71+ static const uint64_t kFNVOffsetBasis = 14695981039346656037ULL ;
72+ static const uint64_t kFNVPrime = 1099511628211ULL ;
73+
74+ static inline uint64_t fnvMixByte (uint64_t hash, uint8_t byte)
75+ {
76+ hash ^= byte;
77+ hash *= kFNVPrime ;
78+ return hash;
79+ }
80+
81+ static inline uint64_t fnvMixUInt64 (uint64_t hash, uint64_t value)
82+ {
83+ for (int i = 0 ; i < 8 ; i++) {
84+ hash = fnvMixByte (hash, (uint8_t )(value & 0xFF ));
85+ value >>= 8 ;
86+ }
87+ return hash;
88+ }
89+
90+ static inline uint64_t fnvMixString (uint64_t hash, NSString *string)
91+ {
92+ if (!string)
93+ return hash;
94+ const char *utf8 = [string UTF8String ];
95+ while (*utf8) {
96+ hash = fnvMixByte (hash, (uint8_t )*utf8++);
97+ }
98+ return hash;
99+ }
100+
101+ uint64_t ENRMSignatureForNode (MarkdownASTNode *node)
70102{
71103 if (!node)
72- return @" " ;
104+ return kFNVOffsetBasis ;
105+
106+ uint64_t hash = kFNVOffsetBasis ;
107+ hash = fnvMixUInt64 (hash, (uint64_t )node.type );
108+ hash = fnvMixString (hash, node.content );
73109
74- NSMutableString *signature = [NSMutableString stringWithFormat: @" %ld |%@ |" , (long )node.type, node.content ?: @" " ];
75110 NSArray *keys = [[node.attributes allKeys ] sortedArrayUsingSelector: @selector (compare: )];
76111 for (NSString *key in keys) {
77- [signature appendFormat: @" %@ =%@ ;" , key, node.attributes[key]];
112+ hash = fnvMixString (hash, key);
113+ hash = fnvMixString (hash, node.attributes [key]);
78114 }
79- [signature appendString: @" [ " ];
115+
80116 for (MarkdownASTNode *child in node.children ) {
81- [signature appendString: ENRMSignatureForNode (child)];
82- [signature appendString: @" ," ];
117+ hash = fnvMixUInt64 (hash, ENRMSignatureForNode (child));
83118 }
84- [signature appendString: @" ] " ];
85- return signature ;
119+
120+ return hash ;
86121}
87122
88- NSString * ENRMSignatureForNodes (NSArray <MarkdownASTNode *> *nodes)
123+ uint64_t ENRMSignatureForNodes (NSArray <MarkdownASTNode *> *nodes)
89124{
90- NSMutableString *signature = [ NSMutableString string ] ;
125+ uint64_t hash = kFNVOffsetBasis ;
91126 for (MarkdownASTNode *node in nodes) {
92- [signature appendString: ENRMSignatureForNode (node)];
93- [signature appendString: @" |" ];
127+ hash = fnvMixUInt64 (hash, ENRMSignatureForNode (node));
94128 }
95- return signature ;
129+ return hash ;
96130}
0 commit comments