Skip to content

Add fast indexing support to windows-ecma335 #3562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions crates/libs/ecma335/src/reader/blob.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::*;

pub struct Blob<'a> {
file: &'a File,
index: &'a Index,
file: usize,
slice: &'a [u8],
}

Expand All @@ -26,8 +27,8 @@ impl std::ops::Deref for Blob<'_> {
}

impl<'a> Blob<'a> {
pub fn new(file: &'a File, slice: &'a [u8]) -> Self {
Self { file, slice }
pub fn new(index: &'a Index, file: usize, slice: &'a [u8]) -> Self {
Self { index, file, slice }
}

fn peek(&self) -> (usize, usize) {
Expand All @@ -47,7 +48,7 @@ impl<'a> Blob<'a> {
}

pub fn decode<D: Decode<'a>>(&mut self) -> D {
D::decode(self.file, self.read_compressed())
D::decode(self.index, self.file, self.read_compressed())
}

pub fn try_read(&mut self, expected: usize) -> bool {
Expand All @@ -68,7 +69,11 @@ impl<'a> Blob<'a> {
break;
} else {
self.offset(offset);
mods.push(TypeDefOrRef::decode(self.file, self.read_compressed()))
mods.push(TypeDefOrRef::decode(
self.index,
self.file,
self.read_compressed(),
))
}
}
mods
Expand Down
10 changes: 5 additions & 5 deletions crates/libs/ecma335/src/reader/codes.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;

pub trait Decode<'a> {
fn decode(file: &'a File, code: usize) -> Self;
fn decode(index: &'a Index, file: usize, code: usize) -> Self;
}

macro_rules! code {
Expand All @@ -11,10 +11,10 @@ macro_rules! code {
$($table($table<'a>),)*
}
impl<'a> Decode<'a> for $name<'a> {
fn decode(file: &'a File, code: usize) -> Self {
fn decode(index: &'a Index, file: usize, code: usize) -> Self {
let (kind, row) = (code & ((1 << $size) - 1), (code >> $size) - 1);
match kind {
$($code => Self::$table($table(Row::new(file, row))),)*
$($code => Self::$table($table(Row::new(index, file, row))),)*
rest => panic!("{rest:?}"),
}
}
Expand All @@ -23,7 +23,7 @@ macro_rules! code {
#[allow(dead_code)]
pub fn encode(&self) -> usize {
match self {
$(Self::$table(row) => (row.index() + 1) << $size | $code,)*
$(Self::$table(row) => (row.pos() + 1) << $size | $code,)*
}
}
}
Expand Down Expand Up @@ -109,7 +109,7 @@ code! { TypeOrMethodDef(1)
(TypeDef, 0)
}

impl<'a> TypeDefOrRef<'a> {
impl TypeDefOrRef<'_> {
pub fn namespace(&self) -> &str {
match self {
Self::TypeDef(row) => row.namespace(),
Expand Down
103 changes: 27 additions & 76 deletions crates/libs/ecma335/src/reader/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,6 @@ pub struct File {
tables: [Table; 17],
}

impl std::fmt::Debug for File {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::write!(f, "{:?}", self.bytes.as_ptr())
}
}

impl std::hash::Hash for File {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.bytes.as_ptr().hash(state);
}
}

impl PartialEq for File {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.bytes.as_ptr(), other.bytes.as_ptr())
}
}

impl Eq for File {}

impl Ord for File {
fn cmp(&self, other: &Self) -> Ordering {
self.bytes.as_ptr().cmp(&other.bytes.as_ptr())
}
}

impl PartialOrd for File {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

unsafe impl Sync for File {}

impl File {
pub fn read<P: AsRef<std::path::Path>>(path: P) -> Option<Self> {
std::fs::read(path).ok().and_then(Self::new)
Expand Down Expand Up @@ -535,7 +501,7 @@ impl File {
std::str::from_utf8(&bytes[..nul_pos]).expect("expected valid utf-8 C-string")
}

pub(crate) fn blob(&self, row: usize, table: usize, column: usize) -> Blob {
pub(crate) fn blob(&self, row: usize, table: usize, column: usize) -> &[u8] {
let offset = self.blobs + self.usize(row, table, column);
let initial_byte = self.bytes[offset];

Expand All @@ -553,36 +519,34 @@ impl File {
}

let offset = offset + blob_size_bytes;
Blob::new(self, &self.bytes[offset..offset + blob_size])
}

pub fn row<'a, R: AsRow<'a>>(&'a self, row: usize) -> R {
R::from_row(Row::new(self, row))
&self.bytes[offset..offset + blob_size]
}

pub(crate) fn list<'a, R: AsRow<'a>>(
&'a self,
pub(crate) fn list(
&self,
row: usize,
table: usize,
column: usize,
) -> RowIterator<'a, R> {
other_table: usize,
) -> std::ops::Range<usize> {
let first = self.usize(row, table, column) - 1;
let next = row + 1;
let last = if next < self.tables[table].len {
self.usize(next, table, column) - 1
} else {
self.tables[R::TABLE].len
self.tables[other_table].len
};
RowIterator::new(self, first..last)
first..last
}

pub(crate) fn equal_range<'a, L: AsRow<'a>>(
&'a self,
pub(crate) fn equal_range(
&self,
table: usize,
column: usize,
value: usize,
) -> RowIterator<'a, L> {
) -> std::ops::Range<usize> {
let mut first = 0;
let mut last = self.tables[L::TABLE].len;
let mut last = self.tables[table].len;
let mut count = last;

loop {
Expand All @@ -593,7 +557,7 @@ impl File {

let count2 = count / 2;
let middle = first + count2;
let middle_value = self.usize(middle, L::TABLE, column);
let middle_value = self.usize(middle, table, column);

match middle_value.cmp(&value) {
Ordering::Less => {
Expand All @@ -602,32 +566,23 @@ impl File {
}
Ordering::Greater => count = count2,
Ordering::Equal => {
let first2 = self.lower_bound_of(L::TABLE, first, middle, column, value);
let first2 = self.lower_bound(table, first, middle, column, value);
first += count;
last = self.upper_bound_of(L::TABLE, middle + 1, first, column, value);
last = self.upper_bound(table, middle + 1, first, column, value);
first = first2;
break;
}
}
}

RowIterator::new(self, first..last)
first..last
}

pub(crate) fn parent<'a, P: AsRow<'a>, C: AsRow<'a>>(&'a self, column: usize, child: C) -> P {
P::from_row(Row::new(
self,
self.upper_bound_of(
P::TABLE,
0,
self.tables[P::TABLE].len,
column,
child.index() + 1,
) - 1,
))
pub(crate) fn parent(&self, row: usize, table: usize, column: usize) -> usize {
self.upper_bound(table, 0, self.tables[table].len, column, row + 1) - 1
}

fn lower_bound_of(
fn lower_bound(
&self,
table: usize,
mut first: usize,
Expand All @@ -649,7 +604,7 @@ impl File {
first
}

fn upper_bound_of(
fn upper_bound(
&self,
table: usize,
mut first: usize,
Expand All @@ -671,16 +626,12 @@ impl File {
first
}

pub fn table<'a, R: AsRow<'a>>(&'a self) -> RowIterator<'a, R> {
RowIterator::new(self, 0..self.tables[R::TABLE].len)
}

pub fn TypeDef(&self) -> RowIterator<TypeDef> {
self.table()
pub(crate) fn TypeDef(&self) -> std::ops::Range<usize> {
0..self.tables[TypeDef::TABLE].len
}

pub fn NestedClass<'a>(&'a self) -> RowIterator<'a, NestedClass<'a>> {
self.table()
pub(crate) fn NestedClass(&self) -> std::ops::Range<usize> {
0..self.tables[NestedClass::TABLE].len
}
}

Expand Down Expand Up @@ -733,8 +684,8 @@ impl View for [u8] {

fn view_as_str(&self, offset: usize) -> Option<&[u8]> {
let buffer = &self[offset..];
let index = buffer.iter().position(|c| *c == b'\0')?;
Some(&self[offset..offset + index])
let pos = buffer.iter().position(|c| *c == b'\0')?;
Some(&self[offset..offset + pos])
}

fn is_proper_length<T>(&self, offset: usize) -> Option<()> {
Expand Down
68 changes: 45 additions & 23 deletions crates/libs/ecma335/src/reader/index.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,63 @@
use super::*;

pub struct Index<'a> {
types: BTreeMap<&'a str, BTreeMap<&'a str, Vec<TypeDef<'a>>>>,
nested: HashMap<TypeDef<'a>, Vec<TypeDef<'a>>>,
pub struct Index {
files: Vec<File>,
types: HashMap<String, HashMap<String, Vec<(usize, usize)>>>,
nested: HashMap<(usize, usize), Vec<usize>>,
}

impl<'a> Index<'a> {
pub fn new(files: &'a [File]) -> Self {
let mut types: BTreeMap<&str, BTreeMap<&str, Vec<TypeDef>>> = BTreeMap::new();
let mut nested: HashMap<TypeDef, Vec<TypeDef>> = HashMap::new();
impl Index {
pub fn read<P: AsRef<std::path::Path>>(path: P) -> Option<Self> {
Some(Self::new(vec![File::read(path)?]))
}

pub fn new(files: Vec<File>) -> Self {
let mut types: HashMap<String, HashMap<String, Vec<(usize, usize)>>> = HashMap::new();
let mut nested: HashMap<(usize, usize), Vec<usize>> = HashMap::new();

for file in files {
for def in file.TypeDef() {
let namespace = def.namespace();
for (file_pos, file) in files.iter().enumerate() {
for def_pos in file.TypeDef() {
let namespace = file.str(def_pos, TypeDef::TABLE, 2);

if namespace.is_empty() {
// Skips `<Module>` as well as nested types.
continue;
}

let name = file.str(def_pos, TypeDef::TABLE, 1);

types
.entry(namespace)
.entry(namespace.to_string())
.or_default()
.entry(trim_tick(def.name()))
.entry(trim_tick(name).to_string())
.or_default()
.push(def);
.push((file_pos, def_pos));
}

for map in file.NestedClass() {
let outer = map.outer();
let inner = map.inner();
nested.entry(outer).or_default().push(inner);
let inner = file.usize(map, TypeDef::TABLE, 0);
let outer = file.usize(map, TypeDef::TABLE, 1);
nested.entry((file_pos, outer)).or_default().push(inner);
}
}

Self { types, nested }
Self {
files,
types,
nested,
}
}

pub(crate) fn files(&self, pos: usize) -> &File {
&self.files[pos]
}

pub fn all(&self) -> impl Iterator<Item = TypeDef> + '_ {
self.types
.values()
.flat_map(|types| types.values())
.flatten()
.cloned()
.map(|(file, pos)| TypeDef(Row::new(self, *file, *pos)))
}

pub fn get(&self, namespace: &str, name: &str) -> impl Iterator<Item = TypeDef> + '_ {
Expand All @@ -51,7 +66,7 @@ impl<'a> Index<'a> {
.and_then(|types| types.get(name))
.into_iter()
.flatten()
.cloned()
.map(|(file, pos)| TypeDef(Row::new(self, *file, *pos)))
}

pub fn expect(&self, namespace: &str, name: &str) -> TypeDef {
Expand All @@ -68,11 +83,18 @@ impl<'a> Index<'a> {
}
}

pub fn nested(&self, ty: TypeDef<'a>) -> impl Iterator<Item = TypeDef<'a>> + '_ {
pub fn nested(&self, ty: TypeDef) -> impl Iterator<Item = TypeDef> + '_ {
self.nested
.get(&ty)
.map(|nested| nested.iter())
.unwrap_or_else(|| [].iter())
.get(&(ty.0.file, ty.0.pos))
.into_iter()
.flatten()
.cloned()
.map(move |pos| {
TypeDef(Row {
index: self,
file: ty.0.file,
pos,
})
})
}
}
Loading