1pub mod simd;
6
7macro_rules! pick {
9 ($(if #[cfg($($test:meta),*)] {
10 $($if_tokens:tt)*
11 })else+ else {
12 $($else_tokens:tt)*
13 }) => {
14 pick!{
15 @__forests [ ] ;
16 $( [ {$($test),*} {$($if_tokens)*} ], )*
17 [ { } {$($else_tokens)*} ],
18 }
19 };
20 (if #[cfg($($if_meta:meta),*)] {
21 $($if_tokens:tt)*
22 } $(else if #[cfg($($else_meta:meta),*)] {
23 $($else_tokens:tt)*
24 })*) => {
25 pick!{
26 @__forests [ ] ;
27 [ {$($if_meta),*} {$($if_tokens)*} ],
28 $( [ {$($else_meta),*} {$($else_tokens)*} ], )*
29 }
30 };
31 (@__forests [$($not:meta,)*];) => {
32 };
34 (@__forests [$($not:meta,)*]; [{$($m:meta),*} {$($tokens:tt)*}], $($rest:tt)*) => {
35 #[cfg(all( $($m,)* not(any($($not),*)) ))]
36 pick!{ @__identity $($tokens)* }
37 pick!{ @__forests [ $($not,)* $($m,)* ] ; $($rest)* }
38 };
39 (@__identity $($tokens:tt)*) => {
40 $($tokens)*
41 };
42}
43
44pub(crate) use pick;
45
46pub struct BufBlockReader<'a, const STEP_SIZE: usize> {
47 buf: &'a [u8],
48 len_minus_step: usize,
49 idx: usize,
50}
51
52impl<'a, const STEP_SIZE: usize> BufBlockReader<'a, STEP_SIZE> {
53 pub fn new(buf: &'a [u8]) -> Self {
54 Self {
55 len_minus_step: buf.len().saturating_sub(STEP_SIZE),
56 idx: 0,
57 buf,
58 }
59 }
60
61 pub fn has_full_block(&self) -> bool {
62 self.idx < self.len_minus_step
63 }
64
65 pub fn full_block(&self) -> &'a [u8] {
66 &self.buf[self.idx..]
67 }
68
69 pub fn get_remainder(&self, dest: &mut [u8]) -> usize {
70 if self.buf.len() == self.idx {
71 return 0;
72 }
73
74 dest[..STEP_SIZE].fill(0x20);
75 let remainder = &self.buf[self.idx..];
76 dest[..remainder.len()].copy_from_slice(remainder);
77 self.buf.len() - self.idx
78 }
79
80 pub fn advance(&mut self) {
81 self.idx += STEP_SIZE;
82 }
83}
84
85#[derive(Default)]
86struct JsonEscapeScanner {
87 next_is_escaped: u64,
88}
89
90struct EscapedAndEscape {
91 escaped: u64,
94}
95
96impl JsonEscapeScanner {
97 fn new() -> Self {
98 Self::default()
99 }
100
101 fn next(&mut self, backslash: u64) -> EscapedAndEscape {
102 let escape_and_terminal_code =
106 Self::next_escape_and_terminal_code(backslash & !self.next_is_escaped);
107 let escaped = escape_and_terminal_code ^ (backslash | self.next_is_escaped);
108 let escape = escape_and_terminal_code & backslash;
109 self.next_is_escaped = escape >> 63;
110 EscapedAndEscape { escaped }
111 }
112
113 fn next_escape_and_terminal_code(potential_escape: u64) -> u64 {
114 let maybe_escaped = potential_escape << 1;
119
120 const ODD_BITS: u64 = 0xAAAAAAAAAAAAAAAA;
121
122 let maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS;
123 let even_series_codes_and_odd_bits =
124 maybe_escaped_and_odd_bits.wrapping_sub(potential_escape);
125
126 even_series_codes_and_odd_bits ^ ODD_BITS
127 }
128}
129
130#[derive(Debug)]
132pub struct JsonStringBlock {
133 escaped: u64,
135 quote: u64,
137 in_string: u64,
139}
140
141impl JsonStringBlock {
142 pub fn new(escaped: u64, quote: u64, in_string: u64) -> Self {
143 Self {
144 escaped,
145 quote,
146 in_string,
147 }
148 }
149
150 pub fn escaped(&self) -> u64 {
152 self.escaped
153 }
154
155 pub fn quote(&self) -> u64 {
157 self.quote
158 }
159
160 pub fn string_content(&self) -> u64 {
162 self.in_string & !self.quote
163 }
164
165 pub fn non_quote_inside_string(&self, mask: u64) -> u64 {
167 mask & self.in_string
168 }
169
170 pub fn non_quote_outside_string(&self, mask: u64) -> u64 {
172 mask & !self.in_string
173 }
174
175 pub fn string_tail(&self) -> u64 {
177 self.in_string ^ self.quote
178 }
179}
180
181#[derive(Debug, PartialEq, Eq)]
182pub enum Error {
183 InputTooLong,
184 UnclosedString,
185 UnmatchedBrace(usize),
186}
187
188pub struct JsonStringScanner {
189 escape_scanner: JsonEscapeScanner,
191 prev_in_string: u64,
193}
194
195impl Default for JsonStringScanner {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201impl JsonStringScanner {
202 pub fn new() -> Self {
203 Self {
204 escape_scanner: JsonEscapeScanner::new(),
205 prev_in_string: 0,
206 }
207 }
208
209 pub fn next(&mut self, input: &simd::Simd8x64<u8>) -> JsonStringBlock {
216 let backslash = input.eq(b'\\');
217 let escaped = self.escape_scanner.next(backslash).escaped;
218 let quote = input.eq(b'"') & !escaped;
219
220 let in_string = prefix_xor(quote) ^ self.prev_in_string;
224
225 self.prev_in_string = (in_string as i64).wrapping_shr(63) as u64;
227
228 JsonStringBlock::new(escaped, quote, in_string)
229 }
230
231 pub fn finish(&self) -> Result<(), Error> {
233 if self.prev_in_string != 0 {
234 Err(Error::UnclosedString)
235 } else {
236 Ok(())
237 }
238 }
239}
240
241fn prefix_xor(mask: u64) -> u64 {
245 let mut result = mask;
246 result ^= result << 1;
248 result ^= result << 2;
249 result ^= result << 4;
250 result ^= result << 8;
251 result ^= result << 16;
252 result ^= result << 32;
253 result
254}
255
256#[derive(Debug)]
258pub struct JsonCharacterBlock {
259 whitespace: u64,
260 op: u64,
261}
262
263#[cfg(all(feature = "simd", target_arch = "x86_64"))]
264type CharacterClassifier = fn(&simd::Simd8x64<u8>) -> JsonCharacterBlock;
265
266mod classify {
267 use super::*;
268
269 #[inline(always)]
270 #[allow(dead_code, reason = "fallback classifier used on some targets")]
271 pub fn classify_by_comparison(
272 input: &simd::Simd8x64<u8>,
273 ) -> JsonCharacterBlock {
274 let whitespace =
275 input.eq(b' ') | input.eq(b'\t') | input.eq(b'\n') | input.eq(b'\r');
276 let op = input.eq(b',')
277 | input.eq(b'[')
278 | input.eq(b'{')
279 | input.eq(b']')
280 | input.eq(b'}');
281
282 JsonCharacterBlock { whitespace, op }
283 }
284
285 #[allow(dead_code, reason = "test/reference classifier")]
286 pub fn classify_scalar(input: &simd::Simd8x64<u8>) -> JsonCharacterBlock {
287 let mut buf = [0; 64];
288 input.store(&mut buf);
289
290 let mut whitespace = 0;
291 let mut op = 0;
292 for (i, b) in buf.into_iter().enumerate() {
293 let bit = 1u64 << i;
294 match b {
295 b' ' | b'\t' | b'\n' | b'\r' => whitespace |= bit,
296 b',' | b'[' | b'{' | b']' | b'}' => op |= bit,
297 _ => {}
298 }
299 }
300
301 JsonCharacterBlock { whitespace, op }
302 }
303
304 #[cfg(all(
305 feature = "simd",
306 target_arch = "aarch64",
307 target_feature = "neon"
308 ))]
309 #[inline(always)]
310 fn classify_aarch64_neon_bits(
311 input: &simd::Simd8x64<u8>,
312 ) -> simd::Simd8x64<u8> {
313 use simd::width_128::Simd8;
314 use simd::width_128::make_u8x16;
315 let table1 =
316 make_u8x16(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0).into();
317 let table2 =
318 make_u8x16(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0).into();
319
320 simd::Simd8x64::from_chunks([
321 (input.chunks[0] & Simd8::<u8>::splat(0xf)).lookup_16_table(table1)
322 & (input.chunks[0].shr::<4>()).lookup_16_table(table2),
323 (input.chunks[1] & Simd8::<u8>::splat(0xf)).lookup_16_table(table1)
324 & (input.chunks[1].shr::<4>()).lookup_16_table(table2),
325 (input.chunks[2] & Simd8::<u8>::splat(0xf)).lookup_16_table(table1)
326 & (input.chunks[2].shr::<4>()).lookup_16_table(table2),
327 (input.chunks[3] & Simd8::<u8>::splat(0xf)).lookup_16_table(table1)
328 & (input.chunks[3].shr::<4>()).lookup_16_table(table2),
329 ])
330 }
331
332 #[cfg(all(
333 feature = "simd",
334 target_arch = "aarch64",
335 target_feature = "neon"
336 ))]
337 #[inline(always)]
338 fn aarch64_neon_bits_mask(v: &simd::Simd8x64<u8>, bits: u8) -> u64 {
339 use simd::width_128::Simd8x64;
340
341 Simd8x64::from_chunks([
342 v.chunks[0].any_bits_set(bits.into()),
343 v.chunks[1].any_bits_set(bits.into()),
344 v.chunks[2].any_bits_set(bits.into()),
345 v.chunks[3].any_bits_set(bits.into()),
346 ])
347 .to_bitmask()
348 }
349
350 #[cfg(all(
351 feature = "simd",
352 target_arch = "aarch64",
353 target_feature = "neon"
354 ))]
355 #[inline(always)]
356 pub fn classify_aarch64_neon(
358 input: &simd::Simd8x64<u8>,
359 ) -> JsonCharacterBlock {
360 let v = classify_aarch64_neon_bits(input);
361 let op = aarch64_neon_bits_mask(&v, 0x3);
362 let whitespace = aarch64_neon_bits_mask(&v, 0x18);
363 JsonCharacterBlock { whitespace, op }
364 }
365
366 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
367 #[target_feature(enable = "ssse3")]
368 pub unsafe fn classify_x86_ssse3(
370 input: &simd::width_128::Simd8x64<u8>,
371 ) -> JsonCharacterBlock {
372 use simd::width_128::Simd8x64;
373 use simd::width_128::make_u8x16;
374
375 use crate::simd::width_128::Simd8;
376 let whitespace_table = make_u8x16(
379 b' ', 100, 100, 100, 17, 100, 113, 2, 100, b'\t', b'\n', 112, 100, b'\r',
380 100, 100,
381 );
382 let op_table = make_u8x16(
401 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b'{', b',', b'}', 0, 0, );
404
405 #[inline(always)]
406 fn shuffle(table: wide::u8x16, input: Simd8<u8>) -> Simd8<u8> {
407 unsafe {
411 std::arch::x86_64::_mm_shuffle_epi8(
412 bytemuck::must_cast(table),
413 bytemuck::must_cast(input.base),
414 )
415 }
416 .into()
417 }
418
419 let whitespace = input.cmp_eq_mask(&Simd8x64::from_chunks([
424 shuffle(whitespace_table, input.chunks[0]),
425 shuffle(whitespace_table, input.chunks[1]),
426 shuffle(whitespace_table, input.chunks[2]),
427 shuffle(whitespace_table, input.chunks[3]),
428 ]));
429
430 let curlified = Simd8x64::from_chunks([
431 input.chunks[0] | 0x20.into(),
432 input.chunks[1] | 0x20.into(),
433 input.chunks[2] | 0x20.into(),
434 input.chunks[3] | 0x20.into(),
435 ]);
436
437 let op = curlified.cmp_eq_mask(&Simd8x64::from_chunks([
438 shuffle(op_table, curlified.chunks[0]),
439 shuffle(op_table, curlified.chunks[1]),
440 shuffle(op_table, curlified.chunks[2]),
441 shuffle(op_table, curlified.chunks[3]),
442 ]));
443
444 JsonCharacterBlock { whitespace, op }
445 }
446
447 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
448 fn classify_x86_ssse3_dispatch(
449 input: &simd::width_128::Simd8x64<u8>,
450 ) -> JsonCharacterBlock {
451 unsafe { classify_x86_ssse3(input) }
454 }
455
456 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
457 pub fn runtime_classifier() -> CharacterClassifier {
458 static CLASSIFIER: std::sync::OnceLock<CharacterClassifier> =
459 std::sync::OnceLock::new();
460 *CLASSIFIER.get_or_init(|| {
461 if std::is_x86_feature_detected!("ssse3") {
462 classify_x86_ssse3_dispatch
463 } else {
464 classify_by_comparison
465 }
466 })
467 }
468}
469
470impl JsonCharacterBlock {
471 #[inline(always)]
472 pub fn classify(input: &simd::Simd8x64<u8>) -> Self {
474 #[cfg(all(
475 feature = "simd",
476 target_arch = "aarch64",
477 target_feature = "neon"
478 ))]
479 {
480 classify::classify_aarch64_neon(input)
481 }
482 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
483 {
484 (classify::runtime_classifier())(input)
485 }
486 #[cfg(not(any(
487 all(feature = "simd", target_arch = "aarch64", target_feature = "neon"),
488 all(feature = "simd", target_arch = "x86_64")
489 )))]
490 {
491 classify::classify_by_comparison(input)
492 }
493 }
494
495 pub fn whitespace(&self) -> u64 {
497 self.whitespace
498 }
499
500 pub fn op(&self) -> u64 {
502 self.op
503 }
504
505 pub fn scalar(&self) -> u64 {
507 !(self.op() | self.whitespace())
508 }
509}
510
511pub(crate) struct JsonScanner {
517 string_scanner: JsonStringScanner,
518 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
519 character_classifier: CharacterClassifier,
520}
521
522impl JsonScanner {
523 pub fn new() -> Self {
524 Self {
525 string_scanner: JsonStringScanner::new(),
526 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
527 character_classifier: classify::runtime_classifier(),
528 }
529 }
530
531 #[inline(always)]
532 pub fn next(
533 &mut self,
534 input: &simd::Simd8x64<u8>,
535 ) -> (JsonStringBlock, JsonCharacterBlock) {
536 let strings = self.string_scanner.next(input);
537 #[cfg(all(feature = "simd", target_arch = "x86_64"))]
538 let characters = (self.character_classifier)(input);
539 #[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
540 let characters = JsonCharacterBlock::classify(input);
541
542 (strings, characters)
543 }
544
545 #[inline(always)]
546 pub fn finish(&self) -> Result<(), Error> {
547 self.string_scanner.finish()
548 }
549}
550
551pub(crate) struct BitIndexer {
552 last_was_quote: bool,
553}
554
555#[inline(always)]
556fn zero_leading_bit(rev_bits: u64, leading_zeroes: u32) -> u64 {
557 rev_bits ^ (0x8000000000000000u64.wrapping_shr(leading_zeroes))
558}
559
560impl BitIndexer {
561 #[inline(always)]
562 pub fn new() -> Self {
563 Self {
564 last_was_quote: false,
565 }
566 }
567
568 #[inline(always)]
569 pub fn write_index(
570 &mut self,
571 index: u32,
572 rev_bits: &mut u64,
573 quotes: u64,
574 tail: &mut Vec<Token>,
575 ) {
576 if *rev_bits == 0 {
577 return;
578 }
579 let lz = rev_bits.leading_zeros();
580 let is_quote = quotes & (1u64.wrapping_shl(lz)) != 0;
581 if is_quote {
582 if self.last_was_quote {
583 tail.last_mut().unwrap().set_end(index + lz);
584 self.last_was_quote = false;
585 } else {
586 tail.push(Token::new(
587 index + lz + 1,
588 index + lz + 1,
589 TokenKind::String,
590 ));
591 self.last_was_quote = true;
592 }
593 } else {
594 tail.push(Token::new(index + lz, index + lz + 1, TokenKind::Operator));
595 self.last_was_quote = is_quote;
596 }
597 *rev_bits = zero_leading_bit(*rev_bits, lz);
598 }
599
600 #[inline(always)]
601 pub fn write_indexes(
602 &mut self,
603 index: u32,
604 rev_bits: &mut u64,
605 quotes: u64,
606 tail: &mut Vec<Token>,
607 ) {
608 self.write_index(index, rev_bits, quotes, tail);
609 self.write_index(index, rev_bits, quotes, tail);
610 self.write_index(index, rev_bits, quotes, tail);
611 self.write_index(index, rev_bits, quotes, tail);
612 }
613
614 #[inline(always)]
615 #[allow(
616 clippy::too_many_arguments,
617 reason = "keeps hot token indexing loop allocation-free"
618 )]
619 pub fn write_indexes_stepped(
620 &mut self,
621 index: u32,
622 rev_bits: &mut u64,
623 cnt: usize,
624 start: usize,
625 end: usize,
626 quotes: u64,
627 tail: &mut Vec<Token>,
628 ) {
629 self.write_indexes(index, rev_bits, quotes, tail);
630 if start + 4 < end && start + 4 < cnt {
631 self.write_indexes(index, rev_bits, quotes, tail);
632 }
633 if start + 8 < end && start + 8 < cnt {
634 self.write_indexes(index, rev_bits, quotes, tail);
635 }
636 if start + 12 < end && start + 12 < cnt {
637 self.write_indexes(index, rev_bits, quotes, tail);
638 }
639 if start + 16 < end && start + 16 < cnt {
640 self.write_indexes(index, rev_bits, quotes, tail);
641 }
642 if start + 20 < end && start + 20 < cnt {
643 self.write_indexes(index, rev_bits, quotes, tail);
644 }
645 }
646
647 #[inline(always)]
648 pub fn write(
649 &mut self,
650 index: u32,
651 bits: u64,
652 quotes: u64,
653 tail: &mut Vec<Token>,
654 ) {
655 if bits == 0 {
656 return;
657 }
658
659 let cnt = bits.count_ones();
660 let mut rev_bits = bits.reverse_bits();
661
662 self.write_indexes_stepped(
663 index,
664 &mut rev_bits,
665 cnt as usize,
666 0,
667 24,
668 quotes,
669 tail,
670 );
671
672 if cnt > 24 {
673 for _ in 24..cnt {
674 self.write_index(index, &mut rev_bits, quotes, tail);
675 }
676 }
677 }
678}
679
680#[derive(Debug, Clone, Copy, PartialEq, Eq)]
681pub(crate) struct Token {
682 data: u64,
683}
684
685impl Token {
686 pub fn new(start: u32, end: u32, kind: TokenKind) -> Self {
687 Self {
688 data: (start as u64)
689 | ((end as u64 & 0x7FFFFFFFFFFFFFFF) << 32)
690 | ((kind as u64) << 63),
691 }
692 }
693
694 pub fn start(self) -> u32 {
695 (self.data & 0xFFFFFFFF) as u32
696 }
697 pub fn end(self) -> u32 {
698 ((self.data & 0x7FFFFFFFFFFFFFFF) >> 32) as u32
699 }
700
701 pub fn set_end(&mut self, end: u32) {
702 self.data = (self.data & 0x80000000_FFFFFFFF)
703 | (((end as u64) & 0x7FFFFFFFFFFFFFFF) << 32);
704 }
705
706 pub fn kind(self) -> TokenKind {
707 match self.data >> 63 {
708 0 => TokenKind::Operator,
709 1 => TokenKind::String,
710 _ => unreachable!(),
711 }
712 }
713
714 pub fn value<'a>(&self, input: &'a [u8]) -> &'a [u8] {
715 &input[self.start() as usize..self.end() as usize]
716 }
717
718 pub fn string_value<'a>(&self, input: &'a str) -> &'a str {
719 debug_assert_eq!(self.kind(), TokenKind::String);
720 let bytes = self.value(input.as_bytes());
721 unsafe { std::str::from_utf8_unchecked(bytes) }
726 }
727}
728
729#[derive(Debug, Clone, Copy, PartialEq, Eq)]
730pub(crate) enum TokenKind {
731 Operator = 0,
732 String = 1,
733}
734pub(crate) struct Tokenizer<'a> {
735 scanner: JsonScanner,
736 tokens: Vec<Token>,
737 block_reader: BufBlockReader<'a, 64>,
738 idx: u32,
739
740 bit_indexer: BitIndexer,
741}
742
743impl<'a> Tokenizer<'a> {
744 pub fn new(input: &'a [u8]) -> Result<Self, Error> {
745 if input.len() > 0x7FFF_FFFF {
746 return Err(Error::InputTooLong);
747 }
748 Ok(Self {
749 scanner: JsonScanner::new(),
750 tokens: Vec::with_capacity(input.len() / 4),
751 block_reader: BufBlockReader::new(input),
752 idx: 0,
753 bit_indexer: BitIndexer::new(),
754 })
755 }
756
757 #[inline(always)]
758 fn process_json_block(
759 &mut self,
760 strings: JsonStringBlock,
761 characters: JsonCharacterBlock,
762 ) {
763 let ops = characters.op() & !strings.in_string;
764 let quotes = strings.quote;
765 self
766 .bit_indexer
767 .write(self.idx, ops | quotes, quotes, &mut self.tokens);
768 }
769
770 pub fn tokenize(mut self) -> Result<Vec<Token>, Error> {
771 while self.block_reader.has_full_block() {
772 let block = self.block_reader.full_block();
773 let block =
774 simd::Simd8x64::<u8>::load(arrayref::array_ref![block, 0, 64]);
775 let (strings, characters) = self.scanner.next(&block);
776 self.block_reader.advance();
777 self.process_json_block(strings, characters);
778 self.idx += 64;
779 }
780
781 let mut remainder_buf = [0; 64];
782 let _pad = self.block_reader.get_remainder(&mut remainder_buf);
783 let block =
784 simd::Simd8x64::<u8>::load(arrayref::array_ref![&remainder_buf, 0, 64]);
785 let (strings, characters) = self.scanner.next(&block);
786 self.block_reader.advance();
787 self.process_json_block(strings, characters);
788 self.idx += 64;
789 self.scanner.finish()?;
790 Ok(self.tokens)
791 }
792}
793
794fn structural_operator(input: &[u8], token: Token) -> Option<u8> {
795 let byte = input[token.start() as usize];
796 matches!(byte, b'{' | b'}' | b'[' | b']').then_some(byte)
797}
798
799pub(crate) fn pluck_versions_from_tokens(
800 input: &str,
801 tokens: Vec<Token>,
802) -> Result<Versions<'_>, Error> {
803 enum State<'i> {
804 Start,
805 InVersions,
806 WantVersion,
807 InDistTags,
808 WantDistTagValue(&'i str),
809 }
810 let mut state = State::Start;
811 let mut versions = Vec::new();
812
813 let mut version_ranges = Vec::new();
814
815 let mut dist_tags = rustc_hash::FxHashMap::<&str, &str>::default();
816 let mut object_depth = 0;
817 let mut finished_early = false;
818 let input_bytes = input.as_bytes();
819 for token in tokens {
820 match token.kind() {
821 TokenKind::String => {
822 if object_depth == 1 {
823 let v: &[u8] = token.value(input_bytes);
824 if v == b"versions" {
825 state = State::InVersions;
826 } else if v == b"dist-tags" {
827 state = State::InDistTags;
828 }
829 } else if object_depth == 2 && matches!(state, State::InVersions) {
830 versions.push(token.string_value(input));
831 state = State::WantVersion;
832 } else if object_depth == 2 && matches!(state, State::InDistTags) {
833 let key = token.string_value(input);
834 dist_tags.insert(key, "");
835 state = State::WantDistTagValue(key);
836 } else if object_depth == 2
837 && let State::WantDistTagValue(key) = state
838 {
839 let dist_tag = dist_tags.get_mut(key);
840 if let Some(dist_tag) = dist_tag {
841 *dist_tag = token.string_value(input);
842 }
843 state = State::InDistTags;
844 }
845 }
846 TokenKind::Operator => {
847 let Some(v) = structural_operator(input_bytes, token) else {
848 continue;
849 };
850 if v == b'{' {
851 object_depth += 1;
852 if object_depth == 3 && matches!(state, State::WantVersion) {
853 version_ranges.push((token.start(), token.end()));
854 }
855 } else if v == b'}' {
856 if object_depth == 2
857 && matches!(state, State::InVersions | State::InDistTags)
858 {
859 state = State::Start;
860 if !dist_tags.is_empty() && !versions.is_empty() {
861 finished_early = true;
862 break;
863 }
864 } else if object_depth == 3 && matches!(state, State::WantVersion) {
865 if let Some(last) = version_ranges.last_mut() {
866 last.1 = token.end();
867 }
868 state = State::InVersions;
869 }
870 if object_depth == 0 {
871 return Err(Error::UnmatchedBrace(token.start() as usize));
872 }
873 object_depth -= 1;
874 } else if v == b'[' {
875 object_depth += 1;
876 } else if v == b']' {
877 if object_depth == 0 {
878 return Err(Error::UnmatchedBrace(token.start() as usize));
879 }
880 object_depth -= 1;
881 }
882 }
883 }
884 }
885 if !finished_early && object_depth != 0 {
886 return Err(Error::UnmatchedBrace(input.len()));
887 }
888 Ok(Versions {
889 versions,
890 version_ranges,
891 dist_tags,
892 })
893}
894
895#[derive(Debug, Clone)]
896pub struct Versions<'i> {
897 pub versions: Vec<&'i str>,
898 pub version_ranges: Vec<(u32, u32)>,
899 pub dist_tags: rustc_hash::FxHashMap<&'i str, &'i str>,
900}
901
902pub fn pluck_versions(input: &str) -> Result<Versions<'_>, Error> {
903 let tokenizer = Tokenizer::new(input.as_bytes())?;
904 let tokens = tokenizer.tokenize()?;
905 pluck_versions_from_tokens(input, tokens)
906}
907
908#[cfg(test)]
909pub(crate) fn pluck_packument_index_from_tokens(
910 input: &str,
911 tokens: Vec<Token>,
912) -> Result<PackumentIndex<'_>, Error> {
913 let mut indexer = PackumentIndexer::new(input);
914 let input_bytes = input.as_bytes();
915 for token in tokens {
916 match token.kind() {
917 TokenKind::String => {
918 indexer.on_string(token.start(), token.end());
919 }
920 TokenKind::Operator => {
921 let byte = input_bytes[token.start() as usize];
922 match byte {
923 b'{' | b'}' | b'[' | b']' => {
924 indexer.on_operator(token.start(), byte)?
925 }
926 b',' | b':' => indexer.on_separator(byte),
927 _ => {}
928 }
929 }
930 }
931 }
932
933 indexer.finish()
934}
935
936#[derive(Clone, Copy)]
937enum PackumentState<'i> {
938 Start,
939 WantNameValue,
940 WantDenoEtagValue,
941 WantDenoPackumentFormatValue,
942 InVersions,
943 WantVersion(&'i str),
944 InDistTags,
945 WantDistTagValue(&'i str),
946 InTime,
947 WantTimeValue(&'i str),
948}
949
950#[derive(Debug, Clone, Copy, PartialEq, Eq)]
951enum ObjectKind {
952 Unknown,
953 Version,
954 Dist,
955 NpmUser,
956 Attestations,
957}
958
959#[derive(Debug, Clone, Copy, PartialEq, Eq)]
960enum PendingObject {
961 Dist,
962 NpmUser,
963 Attestations,
964}
965
966#[derive(Debug, Clone, Copy, PartialEq, Eq)]
967enum ContainerKind {
968 Array,
969 Object { expect_key: bool },
970}
971
972#[derive(Debug, Default)]
973struct TrustSignals {
974 has_provenance: bool,
975 has_trusted_publisher: bool,
976 has_approver: bool,
977}
978
979impl TrustSignals {
980 fn evidence(&self) -> TrustEvidence {
981 if self.has_approver {
982 TrustEvidence::StagedPublish
983 } else if self.has_trusted_publisher && self.has_provenance {
984 TrustEvidence::TrustedPublisher
985 } else {
986 TrustEvidence::Provenance
987 }
988 }
989}
990
991struct PackumentIndexer<'i> {
992 input: &'i str,
993 state: PackumentState<'i>,
994 name: Option<&'i str>,
995 deno_etag: Option<&'i str>,
996 deno_packument_format: Option<&'i str>,
997 versions: Vec<&'i str>,
998 version_ranges: Vec<(u32, u32)>,
999 dist_tags: rustc_hash::FxHashMap<&'i str, &'i str>,
1000 time: rustc_hash::FxHashMap<&'i str, &'i str>,
1001 trust_signals: rustc_hash::FxHashMap<&'i str, TrustSignals>,
1002 object_depth: usize,
1003 current_version: Option<&'i str>,
1004 object_kinds: Vec<ObjectKind>,
1005 containers: Vec<ContainerKind>,
1006 pending_object: Option<PendingObject>,
1007}
1008
1009impl<'i> PackumentIndexer<'i> {
1010 fn new(input: &'i str) -> Self {
1011 Self {
1012 input,
1013 state: PackumentState::Start,
1014 name: None,
1015 deno_etag: None,
1016 deno_packument_format: None,
1017 versions: Vec::new(),
1018 version_ranges: Vec::new(),
1019 dist_tags: rustc_hash::FxHashMap::default(),
1020 time: rustc_hash::FxHashMap::default(),
1021 trust_signals: rustc_hash::FxHashMap::default(),
1022 object_depth: 0,
1023 current_version: None,
1024 object_kinds: Vec::new(),
1025 containers: Vec::new(),
1026 pending_object: None,
1027 }
1028 }
1029
1030 fn string_value(&self, start: u32, end: u32) -> &'i str {
1031 &self.input[start as usize..end as usize]
1032 }
1033
1034 fn string_bytes(&self, start: u32, end: u32) -> &'i [u8] {
1035 &self.input.as_bytes()[start as usize..end as usize]
1036 }
1037
1038 fn current_string_is_key(&self) -> bool {
1039 matches!(
1040 self.containers.last(),
1041 Some(ContainerKind::Object { expect_key: true })
1042 )
1043 }
1044
1045 fn mark_key_read(&mut self) {
1046 if let Some(ContainerKind::Object { expect_key }) =
1047 self.containers.last_mut()
1048 {
1049 *expect_key = false;
1050 }
1051 }
1052
1053 fn on_string(&mut self, start: u32, end: u32) {
1054 let is_key = self.current_string_is_key();
1055 if is_key {
1056 self.mark_key_read();
1057 }
1058 let v = self.string_bytes(start, end);
1059 if self.object_depth == 1 {
1060 if !is_key && matches!(self.state, PackumentState::WantNameValue) {
1061 self.name = Some(self.string_value(start, end));
1062 self.state = PackumentState::Start;
1063 } else if !is_key
1064 && matches!(self.state, PackumentState::WantDenoEtagValue)
1065 {
1066 self.deno_etag = Some(self.string_value(start, end));
1067 self.state = PackumentState::Start;
1068 } else if !is_key
1069 && matches!(self.state, PackumentState::WantDenoPackumentFormatValue)
1070 {
1071 self.deno_packument_format = Some(self.string_value(start, end));
1072 self.state = PackumentState::Start;
1073 } else if is_key && v == b"name" {
1074 self.state = PackumentState::WantNameValue;
1075 } else if is_key && v == b"_deno.etag" {
1076 self.state = PackumentState::WantDenoEtagValue;
1077 } else if is_key && v == b"_deno.packumentFormat" {
1078 self.state = PackumentState::WantDenoPackumentFormatValue;
1079 } else if is_key && v == b"versions" {
1080 self.state = PackumentState::InVersions;
1081 } else if is_key && v == b"dist-tags" {
1082 self.state = PackumentState::InDistTags;
1083 } else if is_key && v == b"time" {
1084 self.state = PackumentState::InTime;
1085 }
1086 } else if self.object_depth == 2
1087 && is_key
1088 && matches!(self.state, PackumentState::InVersions)
1089 {
1090 let version = self.string_value(start, end);
1091 self.versions.push(version);
1092 self.state = PackumentState::WantVersion(version);
1093 } else if self.object_depth == 2
1094 && is_key
1095 && matches!(self.state, PackumentState::InDistTags)
1096 {
1097 let key = self.string_value(start, end);
1098 self.dist_tags.insert(key, "");
1099 self.state = PackumentState::WantDistTagValue(key);
1100 } else if self.object_depth == 2
1101 && is_key
1102 && matches!(self.state, PackumentState::InTime)
1103 {
1104 let key = self.string_value(start, end);
1105 self.time.insert(key, "");
1106 self.state = PackumentState::WantTimeValue(key);
1107 } else if self.object_depth == 2 && !is_key {
1108 match self.state {
1109 PackumentState::WantDistTagValue(key) => {
1110 let value = self.string_value(start, end);
1111 if let Some(dist_tag) = self.dist_tags.get_mut(key) {
1112 *dist_tag = value;
1113 }
1114 self.state = PackumentState::InDistTags;
1115 }
1116 PackumentState::WantTimeValue(key) => {
1117 let value = self.string_value(start, end);
1118 if let Some(time_value) = self.time.get_mut(key) {
1119 *time_value = value;
1120 }
1121 self.state = PackumentState::InTime;
1122 }
1123 _ => {}
1124 }
1125 } else if is_key && let Some(version) = self.current_version {
1126 match self
1127 .object_kinds
1128 .last()
1129 .copied()
1130 .unwrap_or(ObjectKind::Unknown)
1131 {
1132 ObjectKind::Version => {
1133 if v == b"dist" {
1134 self.pending_object = Some(PendingObject::Dist);
1135 } else if v == b"_npmUser" {
1136 self.pending_object = Some(PendingObject::NpmUser);
1137 } else {
1138 self.pending_object = None;
1139 }
1140 }
1141 ObjectKind::Dist => {
1142 if v == b"attestations" {
1143 self.pending_object = Some(PendingObject::Attestations);
1144 } else {
1145 self.pending_object = None;
1146 }
1147 }
1148 ObjectKind::NpmUser => {
1149 if v == b"approver" {
1150 self.trust_signals.entry(version).or_default().has_approver = true;
1151 } else if v == b"trustedPublisher" {
1152 self
1153 .trust_signals
1154 .entry(version)
1155 .or_default()
1156 .has_trusted_publisher = true;
1157 }
1158 }
1159 ObjectKind::Attestations => {
1160 if v == b"provenance" {
1161 self
1162 .trust_signals
1163 .entry(version)
1164 .or_default()
1165 .has_provenance = true;
1166 }
1167 }
1168 ObjectKind::Unknown => {}
1169 }
1170 }
1171 }
1172
1173 fn on_operator(&mut self, start: u32, byte: u8) -> Result<(), Error> {
1174 if byte == b'{' {
1175 self.object_depth += 1;
1176 self
1177 .containers
1178 .push(ContainerKind::Object { expect_key: true });
1179 let kind = if self.object_depth == 3 {
1180 if let PackumentState::WantVersion(version) = self.state {
1181 self.version_ranges.push((start, start + 1));
1182 self.current_version = Some(version);
1183 ObjectKind::Version
1184 } else {
1185 ObjectKind::Unknown
1186 }
1187 } else {
1188 match (
1189 self.object_kinds.last().copied(),
1190 self.pending_object.take(),
1191 ) {
1192 (Some(ObjectKind::Version), Some(PendingObject::Dist)) => {
1193 ObjectKind::Dist
1194 }
1195 (Some(ObjectKind::Version), Some(PendingObject::NpmUser)) => {
1196 ObjectKind::NpmUser
1197 }
1198 (Some(ObjectKind::Dist), Some(PendingObject::Attestations)) => {
1199 ObjectKind::Attestations
1200 }
1201 _ => ObjectKind::Unknown,
1202 }
1203 };
1204 self.object_kinds.push(kind);
1205 } else if byte == b'}' {
1206 if self.object_depth == 2
1207 && matches!(
1208 self.state,
1209 PackumentState::InVersions
1210 | PackumentState::InDistTags
1211 | PackumentState::InTime
1212 )
1213 {
1214 self.state = PackumentState::Start;
1215 } else if self.object_depth == 3
1216 && matches!(self.state, PackumentState::WantVersion(_))
1217 {
1218 if let Some(last) = self.version_ranges.last_mut() {
1219 last.1 = start + 1;
1220 }
1221 self.state = PackumentState::InVersions;
1222 self.current_version = None;
1223 self.pending_object = None;
1224 }
1225 if self.object_depth == 0 {
1226 return Err(Error::UnmatchedBrace(start as usize));
1227 }
1228 self.object_depth -= 1;
1229 self.object_kinds.pop();
1230 self.containers.pop();
1231 } else if byte == b'[' {
1232 self.object_depth += 1;
1233 self.object_kinds.push(ObjectKind::Unknown);
1234 self.containers.push(ContainerKind::Array);
1235 } else if byte == b']' {
1236 if self.object_depth == 0 {
1237 return Err(Error::UnmatchedBrace(start as usize));
1238 }
1239 self.object_depth -= 1;
1240 self.object_kinds.pop();
1241 self.containers.pop();
1242 }
1243 Ok(())
1244 }
1245
1246 fn on_separator(&mut self, byte: u8) {
1247 if byte == b','
1248 && let Some(ContainerKind::Object { expect_key }) =
1249 self.containers.last_mut()
1250 {
1251 *expect_key = true;
1252 }
1253 }
1254
1255 fn finish(self) -> Result<PackumentIndex<'i>, Error> {
1256 if self.object_depth != 0 {
1257 return Err(Error::UnmatchedBrace(self.input.len()));
1258 }
1259
1260 let trust_evidence = self
1261 .trust_signals
1262 .into_iter()
1263 .filter(|(_, signals)| signals.has_approver || signals.has_provenance)
1264 .map(|(version, signals)| (version, signals.evidence()))
1265 .collect();
1266
1267 Ok(PackumentIndex {
1268 name: self.name,
1269 deno_etag: self.deno_etag,
1270 deno_packument_format: self.deno_packument_format,
1271 versions: self.versions,
1272 version_ranges: self.version_ranges,
1273 dist_tags: self.dist_tags,
1274 time: self.time,
1275 trust_evidence,
1276 })
1277 }
1278}
1279
1280struct StreamingBitIndexer {
1281 string_start: Option<u32>,
1282}
1283
1284impl StreamingBitIndexer {
1285 fn new() -> Self {
1286 Self { string_start: None }
1287 }
1288
1289 #[inline(always)]
1290 fn write(
1291 &mut self,
1292 input: &[u8],
1293 index: u32,
1294 bits: u64,
1295 quotes: u64,
1296 indexer: &mut PackumentIndexer<'_>,
1297 ) -> Result<(), Error> {
1298 if bits == 0 {
1299 return Ok(());
1300 }
1301
1302 let mut bits = bits;
1303 while bits != 0 {
1304 let offset = bits.trailing_zeros();
1305 let pos = index + offset;
1306 let is_quote = quotes & (1u64.wrapping_shl(offset)) != 0;
1307 if is_quote {
1308 if let Some(start) = self.string_start.take() {
1309 indexer.on_string(start, pos);
1310 } else {
1311 self.string_start = Some(pos + 1);
1312 }
1313 } else if let Some(byte) = input.get(pos as usize).copied() {
1314 match byte {
1315 b'{' | b'}' | b'[' | b']' => indexer.on_operator(pos, byte)?,
1316 b',' | b':' => indexer.on_separator(byte),
1317 _ => {}
1318 }
1319 }
1320 bits &= bits - 1;
1321 }
1322 Ok(())
1323 }
1324}
1325
1326#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1327pub enum TrustEvidence {
1328 Provenance,
1329 TrustedPublisher,
1330 StagedPublish,
1331}
1332
1333#[derive(Debug, Clone)]
1334pub struct PackumentIndex<'i> {
1335 pub name: Option<&'i str>,
1336 pub deno_etag: Option<&'i str>,
1337 pub deno_packument_format: Option<&'i str>,
1340 pub versions: Vec<&'i str>,
1341 pub version_ranges: Vec<(u32, u32)>,
1342 pub dist_tags: rustc_hash::FxHashMap<&'i str, &'i str>,
1343 pub time: rustc_hash::FxHashMap<&'i str, &'i str>,
1344 pub trust_evidence: rustc_hash::FxHashMap<&'i str, TrustEvidence>,
1345}
1346
1347pub fn pluck_packument_index(input: &str) -> Result<PackumentIndex<'_>, Error> {
1348 let input_bytes = input.as_bytes();
1349 if input_bytes.len() > 0x7FFF_FFFF {
1350 return Err(Error::InputTooLong);
1351 }
1352
1353 let mut scanner = JsonScanner::new();
1354 let mut block_reader = BufBlockReader::<64>::new(input_bytes);
1355 let mut idx = 0;
1356 let mut bit_indexer = StreamingBitIndexer::new();
1357 let mut packument_indexer = PackumentIndexer::new(input);
1358
1359 while block_reader.has_full_block() {
1360 let block = block_reader.full_block();
1361 let block = simd::Simd8x64::<u8>::load(arrayref::array_ref![block, 0, 64]);
1362 let (strings, characters) = scanner.next(&block);
1363 block_reader.advance();
1364 let ops = characters.op() & !strings.in_string;
1365 bit_indexer.write(
1366 input_bytes,
1367 idx,
1368 ops | strings.quote,
1369 strings.quote,
1370 &mut packument_indexer,
1371 )?;
1372 idx += 64;
1373 }
1374
1375 let mut remainder_buf = [0; 64];
1376 let _pad = block_reader.get_remainder(&mut remainder_buf);
1377 let block =
1378 simd::Simd8x64::<u8>::load(arrayref::array_ref![&remainder_buf, 0, 64]);
1379 let (strings, characters) = scanner.next(&block);
1380 block_reader.advance();
1381 let ops = characters.op() & !strings.in_string;
1382 bit_indexer.write(
1383 input_bytes,
1384 idx,
1385 ops | strings.quote,
1386 strings.quote,
1387 &mut packument_indexer,
1388 )?;
1389 scanner.finish()?;
1390 packument_indexer.finish()
1391}
1392
1393#[cfg(test)]
1394mod tests {
1395 use pretty_assertions::assert_eq;
1396
1397 use super::*;
1398 #[test]
1399 fn zero_leading_bit_works() {
1400 let cases: &[(u64, u64)] = &[(0b0100, 0), (0b0101, 0b0001)];
1401 for (rev_bits, expected) in cases {
1402 let (rev_bits, expected) = (*rev_bits, *expected);
1403 let leading_zeroes = rev_bits.leading_zeros();
1404 let result = zero_leading_bit(rev_bits, leading_zeroes);
1405 assert_eq!(result, expected);
1406 }
1407 }
1408
1409 fn string(start: u32, s: &str) -> Token {
1410 Token::new(start, start + s.len() as u32, TokenKind::String)
1411 }
1412
1413 fn op(start: u32) -> Token {
1414 Token::new(start, start + 1, TokenKind::Operator)
1415 }
1416
1417 struct TokensBuilder {
1418 tokens: Vec<Token>,
1419 }
1420
1421 impl TokensBuilder {
1422 fn new() -> Self {
1423 Self { tokens: Vec::new() }
1424 }
1425
1426 fn then(self, offset: u32, f: impl FnOnce(Self, u32) -> Self) -> Self {
1427 let last_end = self.tokens.last().map_or(0, |t| t.end());
1428 f(self, last_end + offset)
1429 }
1430
1431 fn with_string(mut self, start: u32, s: &str) -> Self {
1432 self.tokens.push(string(start, s));
1433 self
1434 }
1435
1436 fn with_op(mut self, start: u32) -> Self {
1437 self.tokens.push(op(start));
1438 self
1439 }
1440
1441 fn string(self, offset: u32, s: &str) -> Self {
1442 self.then(offset, |b, i| b.with_string(i, s))
1443 }
1444
1445 fn op(self, offset: u32) -> Self {
1446 self.then(offset, |b, i| b.with_op(i))
1447 }
1448
1449 fn build(self) -> Vec<Token> {
1450 self.tokens
1451 }
1452 }
1453
1454 fn assert_tokens_eq(input: &str, expected: Vec<Token>) {
1455 let tokens = Tokenizer::new(input.as_bytes())
1456 .unwrap()
1457 .tokenize()
1458 .unwrap();
1459 assert_eq!(tokens, expected);
1460 }
1461
1462 fn assert_packument_projection_matches_serde(input: &str) {
1463 let index = pluck_packument_index(input).unwrap();
1464 let value: serde_json::Value = serde_json::from_str(input).unwrap();
1465 let object = value.as_object().unwrap();
1466
1467 assert_eq!(index.name, object.get("name").and_then(|v| v.as_str()));
1468
1469 let versions = object["versions"].as_object().unwrap();
1470 assert_eq!(index.versions.len(), versions.len());
1471 assert_eq!(index.version_ranges.len(), versions.len());
1472
1473 for (version, range) in index.versions.iter().zip(&index.version_ranges) {
1474 assert!(
1475 versions.contains_key(*version),
1476 "indexed unknown version {version}"
1477 );
1478 let range_json: serde_json::Value =
1479 serde_json::from_str(&input[range.0 as usize..range.1 as usize])
1480 .unwrap();
1481 assert_eq!(range_json, versions[*version]);
1482 }
1483
1484 let expected_dist_tags = object
1485 .get("dist-tags")
1486 .and_then(|v| v.as_object())
1487 .into_iter()
1488 .flatten()
1489 .filter_map(|(key, value)| {
1490 value.as_str().map(|value| (key.as_str(), value))
1491 })
1492 .collect::<rustc_hash::FxHashMap<_, _>>();
1493 assert_eq!(index.dist_tags, expected_dist_tags);
1494
1495 let expected_time = object
1496 .get("time")
1497 .and_then(|v| v.as_object())
1498 .into_iter()
1499 .flatten()
1500 .filter_map(|(key, value)| {
1501 value.as_str().map(|value| (key.as_str(), value))
1502 })
1503 .collect::<rustc_hash::FxHashMap<_, _>>();
1504 assert_eq!(index.time, expected_time);
1505 }
1506
1507 fn generated_packument(seed: u32) -> String {
1508 let version_count = 1 + seed as usize % 8;
1509 let mut input = String::new();
1510 input.push_str("{\"_rev\":\"");
1511 input.push_str(&seed.to_string());
1512 input.push_str("\",\"name\":\"@scope/pkg-");
1513 input.push_str(&seed.to_string());
1514 input
1515 .push_str("\",\"noise\":{\"versions\":{\"9.9.9\":{}}},\"dist-tags\":{");
1516 input.push_str("\"latest\":\"1.0.0\"");
1517 if version_count > 1 {
1518 input.push_str(",\"beta\":\"1.0.1\"");
1519 }
1520 input.push_str("},\"versions\":{");
1521 for i in 0..version_count {
1522 if i > 0 {
1523 input.push(',');
1524 }
1525 let version = format!("1.0.{i}");
1526 input.push('"');
1527 input.push_str(&version);
1528 input.push_str("\":{\"version\":\"");
1529 input.push_str(&version);
1530 input.push_str("\",\"description\":\"line \\\\n quote \\\" ok\",");
1531 input.push_str("\"dependencies\":{\"dep\":\"^");
1532 input.push_str(&i.to_string());
1533 input.push_str(".0.0\"},");
1534 if i % 3 == 0 {
1535 input.push_str("\"dist\":{\"tarball\":\"https://registry.example/pkg.tgz\",\"attestations\":{\"provenance\":true}},");
1536 }
1537 if i % 3 == 1 {
1538 input.push_str("\"dist\":{\"tarball\":\"https://registry.example/pkg.tgz\",\"attestations\":{\"provenance\":true}},");
1539 input.push_str(
1540 "\"_npmUser\":{\"trustedPublisher\":{\"id\":\"publisher\"}},",
1541 );
1542 }
1543 if i % 3 == 2 {
1544 input.push_str("\"_npmUser\":{\"approver\":{\"name\":\"approver\"}},");
1545 }
1546 input.push_str("\"nested\":{\"dist\":{\"attestations\":{\"provenance\":true}},\"_npmUser\":{\"approver\":true}}}");
1547 }
1548 input.push_str("},\"time\":{\"created\":\"2024-01-01T00:00:00.000Z\"");
1549 for i in 0..version_count {
1550 input.push_str(",\"1.0.");
1551 input.push_str(&i.to_string());
1552 input.push_str("\":\"2024-01-");
1553 input.push_str(&format!("{:02}", i + 2));
1554 input.push_str("T00:00:00.000Z\"");
1555 }
1556 input.push_str("}}");
1557 input
1558 }
1559
1560 #[test]
1561 fn active_classifier_matches_scalar_classifier() {
1562 let mut input = [0u8; 64];
1563 let sample = br#" { "x": [1, 2, {"y": "\n"}] } "#;
1564 input[..sample.len()].copy_from_slice(sample);
1565 input[sample.len()] = b'\r';
1566 let block = simd::Simd8x64::<u8>::load(&input);
1572 let active = JsonCharacterBlock::classify(&block);
1573 let comparison = classify::classify_by_comparison(&block);
1574 let scalar = classify::classify_scalar(&block);
1575
1576 assert_eq!(active.whitespace(), comparison.whitespace());
1577 assert_eq!(active.op(), comparison.op());
1578 assert_eq!(active.whitespace(), scalar.whitespace());
1579 assert_eq!(active.op(), scalar.op());
1580 }
1581
1582 #[test]
1583 fn incomplete_string_works() {
1584 let input = r#"{"versions":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa":{},"bcdefghijkabcd":"asdf"}}"#;
1585
1586 let expected = TokensBuilder::new()
1587 .op(0)
1588 .string(1, "versions")
1589 .op(2)
1590 .string(1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
1591 .op(2) .op(0) .op(0) .string(1, "bcdefghijkabcd")
1595 .string(3, "asdf")
1596 .op(1) .op(0) .build();
1599 assert_tokens_eq(input, expected);
1600
1601 let input = r#"{"versions":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa":{},"bcdefghijkab":"asdf"}}"#;
1602 let expected = TokensBuilder::new()
1603 .op(0)
1604 .string(1, "versions")
1605 .op(2) .string(1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
1607 .op(2) .op(0) .op(0) .string(1, "bcdefghijkab")
1611 .string(3, "asdf")
1612 .op(1) .op(0) .build();
1615 assert_tokens_eq(input, expected);
1616 }
1617
1618 #[test]
1619 fn split_utf8_works() {
1620 let input = r#"{"versions":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa":{},"bcdefghijkabc♥♥":{}}}"#;
1621 let builder = TokensBuilder::new();
1622 let expected = builder
1623 .op(0) .string(1, "versions")
1625 .op(2) .string(1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
1627 .op(2) .op(0) .op(0) .string(1, "bcdefghijkabc♥♥")
1631 .op(2) .op(0) .op(0) .op(0) .build();
1636 assert_tokens_eq(input, expected);
1637 }
1638
1639 #[test]
1640 fn test_pluck_versions() {
1641 let input = r#"{"versions":{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa":{},"bcdefghijkabc♥♥":{}},"dist-tags":{"latest":"foo","bar":"baz"}}"#;
1642 let versions = pluck_versions(input).unwrap();
1643 assert_eq!(
1644 versions.versions,
1645 vec!["aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bcdefghijkabc♥♥"]
1646 );
1647 assert_eq!(
1648 versions.dist_tags,
1649 vec![("latest", "foo"), ("bar", "baz")]
1650 .into_iter()
1651 .collect()
1652 );
1653 }
1654
1655 #[test]
1656 fn test_pluck_packument_index() {
1657 let input = r#"{"name":"pkg","_deno.etag":"etag-1","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"version":"1.0.0","dist":{"attestations":{"provenance":{"x":1}}}},"1.1.0":{"version":"1.1.0","_npmUser":{"trustedPublisher":{"x":1},"approver":{"name":"a"}}}},"time":{"created":"2024-01-01T00:00:00.000Z","modified":"2024-01-03T00:00:00.000Z","1.0.0":"2024-01-02T00:00:00.000Z","1.1.0":"2024-01-03T00:00:00.000Z"}}"#;
1658 let index = pluck_packument_index(input).unwrap();
1659 assert_eq!(index.name, Some("pkg"));
1660 assert_eq!(index.deno_etag, Some("etag-1"));
1661 assert_eq!(index.versions, vec!["1.0.0", "1.1.0"]);
1662 assert_eq!(
1663 index.dist_tags,
1664 vec![("latest", "1.1.0")].into_iter().collect()
1665 );
1666 assert_eq!(
1667 index.time,
1668 vec![
1669 ("created", "2024-01-01T00:00:00.000Z"),
1670 ("modified", "2024-01-03T00:00:00.000Z"),
1671 ("1.0.0", "2024-01-02T00:00:00.000Z"),
1672 ("1.1.0", "2024-01-03T00:00:00.000Z"),
1673 ]
1674 .into_iter()
1675 .collect()
1676 );
1677 assert_eq!(
1678 index.trust_evidence,
1679 vec![
1680 ("1.0.0", TrustEvidence::Provenance),
1681 ("1.1.0", TrustEvidence::StagedPublish),
1682 ]
1683 .into_iter()
1684 .collect()
1685 );
1686
1687 let first_range = index.version_ranges[0];
1688 assert_eq!(
1689 &input[first_range.0 as usize..first_range.1 as usize],
1690 r#"{"version":"1.0.0","dist":{"attestations":{"provenance":{"x":1}}}}"#
1691 );
1692 }
1693
1694 #[test]
1695 fn packument_index_ignores_nested_deno_etag() {
1696 let input = r#"{"name":"pkg","versions":{"1.0.0":{"version":"1.0.0","_deno.etag":"nested"}}}"#;
1697 let index = pluck_packument_index(input).unwrap();
1698 assert_eq!(index.name, Some("pkg"));
1699 assert_eq!(index.deno_etag, None);
1700 assert_eq!(index.versions, vec!["1.0.0"]);
1701 }
1702
1703 #[test]
1704 fn packument_index_plucks_deno_packument_format() {
1705 let input = r#"{"name":"pkg","versions":{"1.0.0":{"version":"1.0.0"}},"time":{},"_deno.packumentFormat":"full"}"#;
1706 let index = pluck_packument_index(input).unwrap();
1707 assert_eq!(index.deno_packument_format, Some("full"));
1708 assert_eq!(index.versions, vec!["1.0.0"]);
1709 assert!(index.time.is_empty());
1710 }
1711
1712 #[test]
1713 fn packument_index_ignores_nested_deno_packument_format() {
1714 let input = r#"{"name":"pkg","versions":{"1.0.0":{"version":"1.0.0","_deno.packumentFormat":"full"}}}"#;
1715 let index = pluck_packument_index(input).unwrap();
1716 assert_eq!(index.deno_packument_format, None);
1717 assert_eq!(index.versions, vec!["1.0.0"]);
1718 }
1719
1720 #[test]
1721 fn packument_index_distinguishes_keys_from_values() {
1722 let input = r#"{
1723 "name": "pkg",
1724 "description": "versions",
1725 "other": "time",
1726 "versions": {
1727 "1.0.0": {
1728 "version": "1.0.0",
1729 "description": "dist",
1730 "nested": "_npmUser",
1731 "dist": {
1732 "tarball": "provenance",
1733 "attestations": {"provenance": true}
1734 }
1735 }
1736 },
1737 "dist-tags": {
1738 "latest": "1.0.0",
1739 "label": "dist-tags"
1740 },
1741 "time": {
1742 "created": "2024-01-01T00:00:00.000Z",
1743 "1.0.0": "2024-01-02T00:00:00.000Z"
1744 }
1745 }"#;
1746
1747 let index = pluck_packument_index(input).unwrap();
1748 assert_eq!(index.name, Some("pkg"));
1749 assert_eq!(index.versions, vec!["1.0.0"]);
1750 assert_eq!(
1751 index.dist_tags,
1752 vec![("latest", "1.0.0"), ("label", "dist-tags")]
1753 .into_iter()
1754 .collect()
1755 );
1756 assert_eq!(
1757 index.trust_evidence,
1758 vec![("1.0.0", TrustEvidence::Provenance)]
1759 .into_iter()
1760 .collect()
1761 );
1762 }
1763
1764 #[test]
1765 fn packument_index_matches_serde_json_projection() {
1766 let input = r#"{
1767 "noise": {"versions": {"ignored": {}}},
1768 "name": "pkg",
1769 "dist-tags": {"latest": "2.0.0", "beta": "2.1.0-beta.0"},
1770 "versions": {
1771 "1.0.0": {
1772 "version": "1.0.0",
1773 "dist": {"attestations": {"provenance": true}},
1774 "nested": {"_npmUser": {"approver": true}}
1775 },
1776 "2.0.0": {
1777 "version": "2.0.0",
1778 "_npmUser": {"trustedPublisher": {"id": "pub"}},
1779 "dist": {"attestations": {"provenance": true}}
1780 },
1781 "2.1.0-beta.0": {
1782 "version": "2.1.0-beta.0",
1783 "_npmUser": {"approver": {"name": "approver"}}
1784 }
1785 },
1786 "time": {
1787 "created": "2024-01-01T00:00:00.000Z",
1788 "1.0.0": "2024-01-02T00:00:00.000Z",
1789 "2.0.0": "2024-01-03T00:00:00.000Z",
1790 "2.1.0-beta.0": "2024-01-04T00:00:00.000Z"
1791 }
1792 }"#;
1793
1794 let index = pluck_packument_index(input).unwrap();
1795 let value: serde_json::Value = serde_json::from_str(input).unwrap();
1796 let object = value.as_object().unwrap();
1797
1798 assert_eq!(index.name, object.get("name").and_then(|v| v.as_str()));
1799
1800 let expected_versions = object["versions"]
1801 .as_object()
1802 .unwrap()
1803 .keys()
1804 .map(String::as_str)
1805 .collect::<Vec<_>>();
1806 assert_eq!(index.versions, expected_versions);
1807
1808 for version in &index.versions {
1809 let range = index.version_ranges[index
1810 .versions
1811 .iter()
1812 .position(|candidate| candidate == version)
1813 .unwrap()];
1814 let range_json: serde_json::Value =
1815 serde_json::from_str(&input[range.0 as usize..range.1 as usize])
1816 .unwrap();
1817 assert_eq!(range_json, object["versions"][*version]);
1818 }
1819
1820 let expected_dist_tags = object["dist-tags"]
1821 .as_object()
1822 .unwrap()
1823 .iter()
1824 .map(|(key, value)| (key.as_str(), value.as_str().unwrap()))
1825 .collect::<rustc_hash::FxHashMap<_, _>>();
1826 assert_eq!(index.dist_tags, expected_dist_tags);
1827
1828 let expected_time = object["time"]
1829 .as_object()
1830 .unwrap()
1831 .iter()
1832 .map(|(key, value)| (key.as_str(), value.as_str().unwrap()))
1833 .collect::<rustc_hash::FxHashMap<_, _>>();
1834 assert_eq!(index.time, expected_time);
1835
1836 assert_eq!(
1837 index.trust_evidence,
1838 vec![
1839 ("1.0.0", TrustEvidence::Provenance),
1840 ("2.0.0", TrustEvidence::TrustedPublisher),
1841 ("2.1.0-beta.0", TrustEvidence::StagedPublish),
1842 ]
1843 .into_iter()
1844 .collect()
1845 );
1846 }
1847
1848 #[test]
1849 fn packument_index_handles_top_level_fields_in_any_order() {
1850 let input = r#"{
1851 "time": {
1852 "1.0.0": "2024-01-02T00:00:00.000Z",
1853 "created": "2024-01-01T00:00:00.000Z"
1854 },
1855 "metadata": {
1856 "versions": {"ignored": {}},
1857 "time": {"ignored": "2020-01-01T00:00:00.000Z"},
1858 "dist-tags": {"ignored": "0.0.0"}
1859 },
1860 "versions": {
1861 "1.0.0": {
1862 "version": "1.0.0",
1863 "files": [
1864 {"_npmUser": {"approver": true}},
1865 {"dist": {"attestations": {"provenance": true}}}
1866 ],
1867 "dist": {"attestations": {"provenance": true}}
1868 }
1869 },
1870 "name": "pkg",
1871 "dist-tags": {"latest": "1.0.0"}
1872 }"#;
1873
1874 assert_packument_projection_matches_serde(input);
1875 let index = pluck_packument_index(input).unwrap();
1876 assert_eq!(
1877 index.trust_evidence,
1878 vec![("1.0.0", TrustEvidence::Provenance)]
1879 .into_iter()
1880 .collect()
1881 );
1882 }
1883
1884 #[test]
1885 fn packument_index_ignores_trust_evidence_outside_direct_paths() {
1886 let input = r#"{
1887 "name": "pkg",
1888 "versions": {
1889 "1.0.0": {
1890 "version": "1.0.0",
1891 "nested": {
1892 "dist": {"attestations": {"provenance": true}},
1893 "_npmUser": {
1894 "trustedPublisher": {"id": "publisher"},
1895 "approver": {"name": "approver"}
1896 }
1897 }
1898 },
1899 "1.0.1": {
1900 "version": "1.0.1",
1901 "dist": {
1902 "nested": {"attestations": {"provenance": true}}
1903 },
1904 "_npmUser": {
1905 "nested": {
1906 "trustedPublisher": {"id": "publisher"},
1907 "approver": {"name": "approver"}
1908 }
1909 }
1910 }
1911 },
1912 "dist-tags": {"latest": "1.0.1"},
1913 "time": {
1914 "1.0.0": "2024-01-02T00:00:00.000Z",
1915 "1.0.1": "2024-01-03T00:00:00.000Z"
1916 }
1917 }"#;
1918
1919 assert_packument_projection_matches_serde(input);
1920 let index = pluck_packument_index(input).unwrap();
1921 assert!(index.trust_evidence.is_empty());
1922 }
1923
1924 #[test]
1925 fn packument_index_requires_provenance_for_trusted_publisher() {
1926 let input = r#"{
1927 "name": "pkg",
1928 "versions": {
1929 "1.0.0": {
1930 "version": "1.0.0",
1931 "_npmUser": {"trustedPublisher": {"id": "publisher"}}
1932 },
1933 "1.0.1": {
1934 "version": "1.0.1",
1935 "_npmUser": {"trustedPublisher": {"id": "publisher"}},
1936 "dist": {"attestations": {"provenance": true}}
1937 },
1938 "1.0.2": {
1939 "version": "1.0.2",
1940 "_npmUser": {"approver": {"name": "approver"}}
1941 }
1942 }
1943 }"#;
1944
1945 let index = pluck_packument_index(input).unwrap();
1946 assert_eq!(
1947 index.trust_evidence,
1948 vec![
1949 ("1.0.1", TrustEvidence::TrustedPublisher),
1950 ("1.0.2", TrustEvidence::StagedPublish),
1951 ]
1952 .into_iter()
1953 .collect()
1954 );
1955 }
1956
1957 #[test]
1958 fn generated_packuments_match_serde_json_projection() {
1959 for seed in 0..64 {
1960 let input = generated_packument(seed);
1961 assert_packument_projection_matches_serde(&input);
1962
1963 let index = pluck_packument_index(&input).unwrap();
1964 let tokenized_index = pluck_packument_index_from_tokens(
1965 &input,
1966 Tokenizer::new(input.as_bytes())
1967 .unwrap()
1968 .tokenize()
1969 .unwrap(),
1970 )
1971 .unwrap();
1972 assert_eq!(index.name, tokenized_index.name);
1973 assert_eq!(index.deno_etag, tokenized_index.deno_etag);
1974 assert_eq!(index.versions, tokenized_index.versions);
1975 assert_eq!(index.version_ranges, tokenized_index.version_ranges);
1976 assert_eq!(index.dist_tags, tokenized_index.dist_tags);
1977 assert_eq!(index.time, tokenized_index.time);
1978 assert_eq!(index.trust_evidence, tokenized_index.trust_evidence);
1979
1980 let expected_trust_evidence = index
1981 .versions
1982 .iter()
1983 .enumerate()
1984 .map(|(i, version)| {
1985 let evidence = match i % 3 {
1986 0 => TrustEvidence::Provenance,
1987 1 => TrustEvidence::TrustedPublisher,
1988 _ => TrustEvidence::StagedPublish,
1989 };
1990 (*version, evidence)
1991 })
1992 .collect::<rustc_hash::FxHashMap<_, _>>();
1993 assert_eq!(index.trust_evidence, expected_trust_evidence);
1994 }
1995 }
1996
1997 #[test]
1998 fn escaped_quotes() {
1999 let input = r#"{"versions":{"aaa\"bbb":{}}}"#;
2000 let expected = TokensBuilder::new()
2001 .op(0)
2002 .string(1, "versions")
2003 .op(2)
2004 .string(1, r#"aaa\"bbb"#)
2005 .op(2)
2006 .op(0)
2007 .op(0)
2008 .op(0)
2009 .build();
2010 assert_tokens_eq(input, expected);
2011 }
2012
2013 #[test]
2014 fn small_input() {
2015 let input = r#"{"foo":"bar"}"#;
2016 let expected = TokensBuilder::new()
2017 .op(0)
2018 .string(1, "foo")
2019 .string(3, "bar")
2020 .op(1)
2021 .build();
2022 assert_tokens_eq(input, expected);
2023 }
2024
2025 #[test]
2026 fn invalid_input() {
2027 let input = r#"{"versions":{}}}"#;
2028 let _versions = pluck_versions(input);
2029 }
2030
2031 #[test]
2032 fn unclosed_string() {
2033 let input = r#"{"versions:{}}"#;
2034 let error = pluck_versions(input).unwrap_err();
2035 assert_eq!(error, Error::UnclosedString);
2036 }
2037
2038 #[test]
2039 fn unclosed_object() {
2040 let input = r#"{"versions":{"1.0.0":{}"#;
2041 let error = pluck_versions(input).unwrap_err();
2042 assert_eq!(error, Error::UnmatchedBrace(input.len()));
2043
2044 let error = pluck_packument_index(input).unwrap_err();
2045 assert_eq!(error, Error::UnmatchedBrace(input.len()));
2046 }
2047
2048 #[test]
2049 fn malformed_inputs_do_not_panic() {
2050 let inputs = [
2051 "",
2052 "{",
2053 "}",
2054 "[",
2055 "]",
2056 "{{{{",
2057 "}}}}",
2058 r#"{"versions":["1.0.0"]}"#,
2059 r#"{"versions":{"1.0.0":null}}"#,
2060 r#"{"versions":{"1.0.0":[]}}"#,
2061 r#"{"versions":{"1.0.0":{"version":"1.0.0"}"#,
2062 r#"{"dist-tags":{"latest":null},"versions":{}}"#,
2063 r#"{"time":{"1.0.0":null},"versions":{}}"#,
2064 r#"{"name":null,"versions":{}}"#,
2065 r#"{"versions":{"\"":{}},"dist-tags":{"latest":"\""}}"#,
2066 ];
2067
2068 for input in inputs {
2069 let _ = pluck_versions(input);
2070 let _ = pluck_packument_index(input);
2071 }
2072 }
2073
2074 #[test]
2075 fn false_positive_operator_candidates_do_not_panic() {
2076 let input = std::str::from_utf8(&[
2077 123, 34, 118, 101, 114, 115, 105, 111, 110, 115, 34, 58, 123, 34, 49, 46,
2078 48, 46, 48, 34, 58, 123, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58,
2079 34, 49, 46, 48, 34, 34, 34, 34, 34, 34, 35, 34, 34, 34, 34, 105, 115,
2080 101, 34, 205, 132, 221, 137, 101, 114, 115, 105, 111, 110, 34, 34, 34,
2081 34, 34, 34, 34, 34, 34, 42, 34, 34, 34, 34, 34, 34, 34, 34, 34, 92, 0, 0,
2082 0, 34, 34, 34, 34, 34, 34, 92, 0, 0, 0, 34, 34, 34, 34, 34, 34, 34, 34,
2083 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 48, 34, 58,
2084 34, 50, 48, 50, 52, 45, 48, 49, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2085 0, 0, 0, 0, 0, 115, 34, 58, 123, 34, 57, 46, 39, 57, 46, 57, 34, 58, 123,
2086 125, 125, 45, 34, 100, 105, 115, 116, 45, 116, 97, 46, 48, 34, 125,
2087 ])
2088 .unwrap();
2089
2090 let _ = pluck_versions(input);
2091 let _ = pluck_packument_index(input);
2092 }
2093}