Skip to content

Commit 9a6196e

Browse files
authored
Enables GATs iteration in the Element API (#442)
This PR replaces all uses of `Box<dyn Iterator<Item=_> + 'a>` in the `Element` and `ElementRef` APIs with generic associated types. This means that heap allocations are no longer required to iterate over annotations, sequence elements, or struct fields. Because this change uses generic associated types, it also configures the CI tests to use the beta channel of cargo. GATs will be available in the next release of Rust (v1.65, out in November); once that happens, we can and should switch back to stable. (#443) This change also removes the recently added dependency on the `linkhash` crate. That dependency was originally pulled in so structs could remember the order in which their fields were inserted for later iteration. However, it only worked if the struct did not contain duplicate field names. This PR changes the field storage for `Struct` and `StructRef` to store their fields in a `Vec` and also maintain a `HashMap<Symbol, IndexVec>` that remembers all of the indexes at which values for a given field can be found. Further, it moves `Sequence` elements and `Struct` fields into an `Rc` to make clone() operations cheaper. A future PR will modify iterators over collection types to clone the `Rc` and thus remove one of their lifetime constraints, making it much easier to write a recursive iterator over an element tree without constant cloning.
1 parent 1ab131a commit 9a6196e

15 files changed

+531
-222
lines changed

.github/workflows/coverage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
with:
2424
profile: minimal
2525
# nightly can be very volatile--pin this to a version we know works well...
26-
toolchain: nightly-2022-05-01
26+
toolchain: nightly-2022-10-12
2727
override: true
2828
- name: Cargo Test
2929
uses: actions-rs/cargo@v1

.github/workflows/rust.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ jobs:
3434
uses: actions-rs/toolchain@v1
3535
with:
3636
profile: minimal
37-
toolchain: stable
37+
# We're temporarily testing against `beta` to have access to GATs. When Rust v1.65 is released in
38+
# 2022-11, we should change this back to `stable`.
39+
# See: https://github.com/amzn/ion-rust/issues/443
40+
toolchain: beta
41+
components: rustfmt, clippy
3842
override: true
3943
- name: Cargo Build
4044
uses: actions-rs/cargo@v1

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ num-bigint = "0.3"
3838
num-integer = "0.1.44"
3939
num-traits = "0.2"
4040
arrayvec = "0.7"
41-
hashlink = "0.8.1"
4241
smallvec = "1.9.0"
4342

4443
[dev-dependencies]

examples/read_all_values.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ fn read_all_values<R: RawReader>(reader: &mut R) -> IonResult<usize> {
6262
match ion_type {
6363
Struct | List | SExpression => reader.step_in()?,
6464
String => {
65-
let _text = reader.map_string(|_s| ())?;
65+
reader.map_string(|_s| ())?;
6666
}
6767
Symbol => {
6868
let _symbol_id = reader.read_symbol()?;
@@ -83,10 +83,10 @@ fn read_all_values<R: RawReader>(reader: &mut R) -> IonResult<usize> {
8383
let _boolean = reader.read_bool()?;
8484
}
8585
Blob => {
86-
let _blob = reader.map_blob(|_b| ())?;
86+
reader.map_blob(|_b| ())?;
8787
}
8888
Clob => {
89-
let _clob = reader.map_clob(|_c| ())?;
89+
reader.map_clob(|_c| ())?;
9090
}
9191
Null => {}
9292
}

src/binary/int.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ impl DecodedInt {
125125
let empty_leading_bytes: u32 = (magnitude.leading_zeros() - 1) >> 3;
126126
let first_occupied_byte = empty_leading_bytes as usize;
127127

128-
let mut magnitude_bytes: [u8; mem::size_of::<u64>()] = (magnitude as u64).to_be_bytes();
128+
let mut magnitude_bytes: [u8; mem::size_of::<u64>()] = magnitude.to_be_bytes();
129129
let bytes_to_write: &mut [u8] = &mut magnitude_bytes[first_occupied_byte..];
130130
if value < 0 {
131131
bytes_to_write[0] |= 0b1000_0000;

src/binary/non_blocking/raw_binary_reader.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ struct EncodedValue {
8686
impl EncodedValue {
8787
/// Returns the offset of the current value's type descriptor byte.
8888
fn header_offset(&self) -> usize {
89-
self.header_offset as usize
89+
self.header_offset
9090
}
9191

9292
/// Returns the length of this value's header, including the type descriptor byte and any
@@ -734,7 +734,7 @@ impl<A: AsRef<[u8]>> IonReader for RawBinaryBufferReader<A> {
734734
let coefficient_size_in_bytes =
735735
encoded_value.value_length() - exponent_var_int.size_in_bytes();
736736

737-
let exponent = exponent_var_int.value() as i64;
737+
let exponent = exponent_var_int.value();
738738
let coefficient = buffer.read_int(coefficient_size_in_bytes)?;
739739

740740
if coefficient.is_negative_zero() {

src/binary/raw_binary_reader.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ struct EncodedValue {
9191
impl EncodedValue {
9292
/// Returns the offset of the current value's type descriptor byte.
9393
fn header_offset(&self) -> usize {
94-
self.header_offset as usize
94+
self.header_offset
9595
}
9696

9797
/// Returns the length of this value's header, including the type descriptor byte and any
@@ -526,7 +526,7 @@ impl<R: IonDataSource> IonReader for RawBinaryReader<R> {
526526
let coefficient_size_in_bytes =
527527
self.cursor.value.value_length - exponent_var_int.size_in_bytes();
528528

529-
let exponent = exponent_var_int.value() as i64;
529+
let exponent = exponent_var_int.value();
530530
let coefficient = self.read_int(coefficient_size_in_bytes)?;
531531

532532
if coefficient.is_negative_zero() {
@@ -938,7 +938,7 @@ where
938938
#[inline(always)]
939939
fn read_var_int(&mut self) -> IonResult<VarInt> {
940940
let var_int = VarInt::read(&mut self.data_source)?;
941-
self.cursor.bytes_read += var_int.size_in_bytes() as usize;
941+
self.cursor.bytes_read += var_int.size_in_bytes();
942942
Ok(var_int)
943943
}
944944

src/binary/var_uint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ impl VarUInt {
117117
/// unsigned integer
118118
#[inline(always)]
119119
pub fn size_in_bytes(&self) -> usize {
120-
self.size_in_bytes as usize
120+
self.size_in_bytes
121121
}
122122
}
123123

src/symbol_ref.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@ impl AsSymbolRef for Symbol {
7575
}
7676
}
7777

78+
impl AsSymbolRef for &Symbol {
79+
fn as_symbol_ref(&self) -> SymbolRef {
80+
self.text()
81+
.map(SymbolRef::with_text)
82+
.unwrap_or_else(SymbolRef::with_unknown_text)
83+
}
84+
}
85+
86+
impl<'borrow, 'data> AsSymbolRef for &'borrow SymbolRef<'data> {
87+
fn as_symbol_ref(&self) -> SymbolRef<'data> {
88+
// This is essentially free; the only data inside is an Option<&str>
89+
(*self).clone()
90+
}
91+
}
92+
7893
#[cfg(test)]
7994
mod tests {
8095
use super::*;

0 commit comments

Comments
 (0)