Skip to content

Commit 2118924

Browse files
committed
Switched to a simpler but more useful notion of pseudo-structural character; some small fixes.
1 parent 694942e commit 2118924

1 file changed

Lines changed: 22 additions & 34 deletions

File tree

main.cpp

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ never_inline bool find_structural_bits(const u8 * buf, size_t len, ParsedJson &
9696
// persistent state across loop
9797
u64 prev_iter_ends_odd_backslash = 0ULL; // either 0 or 1, but a 64-bit value
9898
u64 prev_iter_inside_quote = 0ULL; // either all zeros or all ones
99-
u64 prev_iter_pseudo_structural_carry = 0ULL;
99+
u64 prev_iter_ends_pseudo_pred = 0ULL;
100100

101101
for (size_t idx = 0; idx < len; idx+=64) {
102102
#ifdef DEBUG
@@ -228,39 +228,27 @@ never_inline bool find_structural_bits(const u8 * buf, size_t len, ParsedJson &
228228
// mask off anything inside quotes
229229
structurals &= ~quote_mask;
230230

231-
// whitespace inside our quotes also doesn't count; otherwise " foo" would generate a spurious
232-
// pseudo-structural-character at 'foo'
233-
whitespace &= ~quote_mask;
234-
235231
// add the real quote bits back into our bitmask as well, so we can
236232
// quickly traverse the strings we've spent all this trouble gathering
237233
structurals |= quote_bits;
238234

239-
// Now, establish "pseudo-structural characters". These are characters that follow a structural
240-
// character followed by zero or more whitespace
241-
// this allows us to discover true/false/null and numbers in any location where they might legally
242-
// occur; it will also create another 'checkpoint' where if a non-quoted region of our input
243-
// has whitespace after a structural character fullowed by a syntax error, we can detect this
244-
// and get an error in a later stage (i.e. the state machine)
245-
246-
// Slightly more painful than it would seem. It's possible that either structurals or whitespace are
247-
// all 1s (e.g. {{{{{{{....{{{{x64, or a really long whitespace). As such there is no safe place
248-
// to add a '1' from the previous iteration without *that* triggering the carry we are looking
249-
// out for, so we must check both carries for overflow
250-
251-
u64 tmp = structurals | whitespace;
252-
u64 tmp2;
253-
bool ps_carry = __builtin_uaddll_overflow(tmp, structurals, &tmp2);
254-
dumpbits(tmp2, "pseudo_structural add calculation first part");
255-
u64 tmp3;
256-
ps_carry = ps_carry | __builtin_uaddll_overflow(tmp2, prev_iter_pseudo_structural_carry, &tmp3);
257-
prev_iter_pseudo_structural_carry = ps_carry ? 0x1ULL : 0x0ULL;
258-
dumpbits(tmp3, "pseudo_structural add calculation after adding carry");
259-
tmp3 &= ~quote_mask;
260-
tmp3 &= ~whitespace;
261-
dumpbits(tmp3, "pseudo_structural add calculation without quotes and whitespace");
262-
dumpbits(structurals, "final structurals without quotes");
263-
structurals |= tmp3;
235+
// Now, establish "pseudo-structural characters". These are non-whitespace characters
236+
// that are (a) outside quotes and (b) have a predecessor that's either whitespace or a structural
237+
// character. This means that subsequent passes will get a chance to encounter the first character
238+
// of every string of non-whitespace and, if we're parsing an atom like true/false/null or a number
239+
// we can stop at the first whitespace or structural character following it.
240+
241+
// a qualified predecessor is something that can happen 1 position before an
242+
// psuedo-structural character
243+
u64 pseudo_pred = structurals | whitespace;
244+
dumpbits(pseudo_pred, "pseudo_pred");
245+
u64 shifted_pseudo_pred = (pseudo_pred << 1) | prev_iter_ends_pseudo_pred;
246+
dumpbits(shifted_pseudo_pred, "shifted_pseudo_pred");
247+
prev_iter_ends_pseudo_pred = pseudo_pred >> 63;
248+
u64 pseudo_structurals = shifted_pseudo_pred & (~whitespace) & (~quote_mask);
249+
dumpbits(pseudo_structurals, "pseudo_structurals");
250+
dumpbits(structurals, "final structurals without pseudos");
251+
structurals |= pseudo_structurals;
264252
dumpbits(structurals, "final structurals and pseudo structurals");
265253

266254
*(u64 *)(pj.structurals + idx/8) = structurals;
@@ -596,28 +584,28 @@ int main(int argc, char * argv[]) {
596584
// as well as a dummy structure and a root structure
597585
// we also potentially write up to 7 iterations beyond
598586
// in our 'cheesy flatten', so make some worst-case
599-
// sapce for that too
587+
// space for that too
600588
u32 max_structures = ROUNDUP_N(p.second, 64) + 2 + 7;
601589
pj.structural_indexes = new u32[max_structures];
602590
pj.nodes = new JsonNode[max_structures];
603591

604-
#if defined(DEBUG) || defined(DEBUG_FSM)
592+
#if defined(DEBUG)
605593
const u32 iterations = 1;
606594
#else
607595
const u32 iterations = 1000;
608596
#endif
609597
vector<double> res;
610598
res.resize(iterations);
611599
for (u32 i = 0; i < iterations; i++) {
600+
auto start = std::chrono::steady_clock::now();
612601
find_structural_bits(p.first, p.second, pj);
613602
flatten_indexes(p.second, pj);
614-
auto start = std::chrono::steady_clock::now();
615603
ape_machine(p.first, p.second, pj);
616604
auto end = std::chrono::steady_clock::now();
617605
std::chrono::duration<double> secs = end - start;
618606
res[i] = secs.count();
619607
}
620-
colorfuldisplay(pj, p.first);
608+
// colorfuldisplay(pj, p.first);
621609
double min_result = *min_element(res.begin(), res.end());
622610
cout << "Min: " << min_result << " bytes read: " << p.second << " Gigabytes/second: " << (p.second) / (min_result * 1000000000.0) << "\n";
623611
return 0;

0 commit comments

Comments
 (0)