-
Notifications
You must be signed in to change notification settings - Fork 14
Make vector attributes private instead of pub(super)
#212
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
base: master
Are you sure you want to change the base?
Conversation
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
WalkthroughThis PR encapsulates FqVector/FqSlice/FqSliceMut internals: internal fields were made private and replaced with public/internal accessor methods and new constructors; all usages across the vector module were updated to call accessors instead of accessing fields directly. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (6)
🧰 Additional context used🧠 Learnings (4)📚 Learning: 2025-12-11T08:35:31.031ZApplied to files:
📚 Learning: 2025-12-14T08:23:53.399ZApplied to files:
📚 Learning: 2025-12-15T22:15:17.779ZApplied to files:
📚 Learning: 2025-12-20T04:55:22.617ZApplied to files:
🧬 Code graph analysis (5)ext/crates/fp/src/vector/fp_wrapper/helpers.rs (1)
ext/crates/fp/src/vector/impl_fqvector.rs (4)
ext/crates/fp/src/vector/iter.rs (1)
ext/crates/fp/src/vector/inner.rs (3)
ext/crates/fp/src/vector/impl_fqslice.rs (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
🔇 Additional comments (25)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ext/crates/fp/src/vector/impl_fqvector.rs (1)
63-82: Unsafe aliasing bug into_byteslittle‑endian branchThe change to compute
num_limbsviaself.fq().number(self.len())is correct, but the existing unsafe block has a soundness issue:unsafe { let buf: &[u8] = std::slice::from_raw_parts_mut(self.limbs().as_ptr() as *mut u8, num_bytes); buffer.write_all(buf)?; }Here
self: &FqVector<_>andself.limbs()comes from an immutable reference, butfrom_raw_parts_mutcreates a&mut [u8]alias to the same memory. Even though you immediately coerce to&[u8]and don’t write through it, creating a mutable reference aliased with an immutable one is UB in Rust.You can fix this by using
from_raw_partson a*const u8instead, which avoids introducing a&mutat all:- if cfg!(target_endian = "little") { - let num_bytes = num_limbs * size_of::<Limb>(); - unsafe { - let buf: &[u8] = - std::slice::from_raw_parts_mut(self.limbs().as_ptr() as *mut u8, num_bytes); - buffer.write_all(buf)?; - } + if cfg!(target_endian = "little") { + let num_bytes = num_limbs * size_of::<Limb>(); + unsafe { + let buf: &[u8] = + std::slice::from_raw_parts(self.limbs().as_ptr() as *const u8, num_bytes); + buffer.write_all(buf)?; + }This keeps the layout assumptions but stays within Rust’s aliasing rules.
🧹 Nitpick comments (4)
ext/crates/fp/src/vector/impl_fqvector.rs (4)
43-61:update_from_bytes: accessor usage is fine; consider propagating I/O errors instead ofunwrapSwitching to
self.limbs_mut()is correct and keeps all mutation inside the accessor API. One thing to consider while you’re here: the little‑endian branch still usesread_exact(...).unwrap(), which will panic instead of returning anio::Error, unlike the big‑endian branch that uses?. You can make behavior consistent and avoid unexpected panics by propagating the error:- unsafe { - let buf: &mut [u8] = - std::slice::from_raw_parts_mut(limbs.as_mut_ptr() as *mut u8, num_bytes); - data.read_exact(buf).unwrap(); - } + unsafe { + let buf: &mut [u8] = + std::slice::from_raw_parts_mut(limbs.as_mut_ptr() as *mut u8, num_bytes); + data.read_exact(buf)?; + }This keeps the API purely
io::Result-based and avoids panics on read failures.
145-157:scaleaccessor migration looks correct; consider early return on zero scalarThe refactor to:
- Assert
self.fq() == c.field(),- Cache
let fq = self.fq();, and- Use
self.limbs_mut()in the looplooks correct and preserves the prior behavior, including the special case for
q = 2.Minor optional tweak: when
c == fq.zero(), you can return immediately afterself.set_to_zero()to avoid running the secondif fq.q() != 2branch with a zero scalar:- if c == fq.zero() { - self.set_to_zero(); - } - if fq.q() != 2 { + if c == fq.zero() { + self.set_to_zero(); + return; + } + if fq.q() != 2 {Not required, just a small clarity/perf improvement.
233-246:copy_from_slicecorrectly rebuilds limb storage via accessorsThe updated implementation:
- Asserts all slice elements share the same field as
self.fq()and that lengths match.- Clears
self.vec_mut()and repopulates it by chunking the slice intofq.entries_per_limb()pieces andfq.packing them.This matches the representation used by constructors and keeps
lenunchanged (and still correct). If you ever want to shave a bit of repetition, you could bindlet vec = self.vec_mut();beforeclear/extend, but that’s optional.
288-337:add_carry_limb/add_carryaccessor refactor maintains behavior; minor perf tidy‑up possibleThe changes to:
- Assert
self.fq() == c.field()andself.fq() == other.fq(),- Use
self.limbs()/self.limbs_mut()for carry propagation, and- Iterate up to
self.limbs().len()inadd_carryall look correct and keep the previous algorithm intact.
If you want to avoid repeated
self.fq()calls in the characteristic‑2 branch, you could cache it once:- assert_eq!(self.fq(), c.field()); - if self.fq().q() == 2 { - let c = self.fq().encode(c); + assert_eq!(self.fq(), c.field()); + let fq = self.fq(); + if fq.q() == 2 { + let c = fq.encode(c);Not necessary for correctness, just a small micro‑optimization/readability improvement.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
ext/crates/fp/src/vector/fp_wrapper/helpers.rs(7 hunks)ext/crates/fp/src/vector/impl_fqslice.rs(7 hunks)ext/crates/fp/src/vector/impl_fqslicemut.rs(10 hunks)ext/crates/fp/src/vector/impl_fqvector.rs(11 hunks)ext/crates/fp/src/vector/inner.rs(2 hunks)ext/crates/fp/src/vector/iter.rs(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
ext/crates/fp/src/vector/fp_wrapper/helpers.rs (1)
ext/crates/fp/src/vector/impl_fqslice.rs (1)
offset(106-110)
ext/crates/fp/src/vector/iter.rs (3)
ext/crates/fp/src/vector/inner.rs (3)
limbs(59-61)limbs(102-104)limbs(129-131)ext/crates/fp/src/field/fp.rs (1)
bit_length(96-103)ext/crates/fp/src/field/field_internal.rs (2)
bit_length(101-101)entries_per_limb(125-127)
ext/crates/fp/src/vector/impl_fqslicemut.rs (2)
ext/crates/fp/src/vector/impl_fqvector.rs (4)
add_basis_element(116-119)as_slice(106-108)add(188-190)new(16-20)ext/crates/fp/src/vector/inner.rs (10)
fq(51-53)fq(86-88)fq(117-119)len(55-57)new(77-84)new(108-115)start(94-96)start(121-123)end(98-100)end(125-127)
ext/crates/fp/src/vector/impl_fqvector.rs (1)
ext/crates/fp/src/vector/inner.rs (8)
from_raw_parts(46-49)fq(51-53)fq(86-88)fq(117-119)len(55-57)limbs(59-61)limbs(102-104)limbs(129-131)
🔇 Additional comments (33)
ext/crates/fp/src/vector/inner.rs (4)
11-39: LGTM! The struct definitions correctly make all fields private, which is the core objective of this PR. This enforces encapsulation and will make future refactoring easier by ensuring all access goes through the accessor methods.
45-74: Accessors forFqVectorare well-designed.The accessor pattern is clean:
from_raw_partsincludes a usefuldebug_assertto validate limb countfq()andlen()are public as they're part of the public APIlimbs(),limbs_mut(),vec_mut(), andlen_mut()are appropriatelypub(super)for internal useNote:
len()is markedconstwhich is correct, butfq()is not. This is fine sinceF: Fieldlikely has non-const trait bounds.
76-105: FqSlice accessors look correct.The
into_limbs(self)method consumesselfbut sinceFqSliceimplementsCopy, this is a valid pattern that returns the borrowed lifetime'a.Minor note:
start()andend()are markedconstwhich is good for potential compile-time evaluation.
107-136: FqSliceMut accessors are consistent with FqSlice.The implementation mirrors
FqSliceappropriately, with the addition oflimbs_mut()for mutable access. Thestart()andend()methods are notconsthere (unlike inFqSlice), but this is acceptable since mutable borrows are involved.ext/crates/fp/src/vector/fp_wrapper/helpers.rs (2)
20-77: Consistent migration to accessor-based API forFqVectorhelpers.All helper methods correctly updated from
self.fq.el(...)toself.fq().el(...). The changes are mechanical and maintain identical functionality.
85-128:FqSliceMuthelpers correctly updated.All accessor usages are consistent with the pattern established in
FqVectorhelpers.ext/crates/fp/src/vector/iter.rs (2)
19-54: Iterator initialization correctly uses accessor methods.The usage of
vec.into_limbs()followed byvec.fq()andvec.start()works correctly becauseFqSliceimplementsCopy. Each accessor call operates on a copy of the slice.The initialization logic properly handles the empty case (lines 24-36) and correctly calculates limb indices and entry positions.
127-158:FqVectorNonZeroIteratorinitialization follows the same correct pattern.Consistent with
FqVectorIterator, this implementation properly uses accessor methods and handles the empty case appropriately.ext/crates/fp/src/vector/impl_fqslice.rs (5)
16-42: Public methods correctly updated to use accessors.The implementations of
prime(),len(),is_empty(), andentry()all properly use the new accessor methods. Theis_empty()method correctly usesconstaccessorsstart()andend().
53-71:is_zero()correctly useslimbs()accessor for read access.The method properly accesses limbs through
self.limbs()for all range checks.
73-100:slice()andto_owned()properly construct new instances via accessors.The
slice()method usesFqSlice::new()withself.into_limbs()and correctly offsets start/end.The
to_owned()method efficiently handles the aligned case by copying limb ranges directly, usinglimbs()andlimbs_mut()accessors appropriately.
103-150: Limb helper methods updated consistently.All internal limb calculation methods (
offset(),limb_range(),limb_masks(), etc.) correctly use accessors for field access.
153-157:From<&FqVector>implementation updated.Uses
v.len()method call instead of direct field access, consistent with the encapsulation goal.ext/crates/fp/src/vector/impl_fqslicemut.rs (7)
13-50: Public methods correctly migrated to accessor pattern.The
prime(),add_basis_element(),set_entry(), andreduce_limbs()methods all properly useself.fq(),self.limbs(), andself.limbs_mut()accessors.The local caching pattern (
let fq = self.fq()) inreduce_limbs()(line 42) is a good practice to avoid repeated accessor calls in loops.
52-100:scale()andset_to_zero()correctly use accessor pattern.Both methods properly use
self.fq()and limb accessors. The localfqbinding inscale()(line 54) optimizes repeated field access.
102-146:add()andadd_tensor()properly validate field equality via accessors.The assertions
assert_eq!(self.fq(), c.field())andassert_eq!(self.fq(), other.fq())correctly use accessor methods for field comparison.
148-219:assign()andadd_shift_none()correctly updated.Both methods properly use accessor-based limb access. The
add_shift_none()method cachesfqlocally (line 185) which is appropriate given the multiplefma_limbandreducecalls.
221-339:add_shift_left()implementation correctly uses accessors.The
AddShiftLeftDatahelper struct methods properly accessother.limbs()for read-only limb access. The main method correctly usesself.fq()for field operations.
341-472:add_shift_right()mirrorsadd_shift_left()pattern correctly.Consistent accessor usage throughout the helper struct and main method implementation.
474-530: Slice and conversion methods properly handle borrows with accessors.The
slice_mut()method (lines 498-508) correctly capturesorig_startbefore callingself.limbs_mut(), avoiding borrow conflicts.Similarly,
copy()(lines 519-524) capturesstartandendbefore the mutable borrow, which is the correct pattern to avoid borrowingselfwhile also takingself.limbs_mut().The
From<&mut FqVector>implementation correctly usesv.len()method.ext/crates/fp/src/vector/impl_fqvector.rs (13)
16-27: Constructors viafrom_raw_partsmaintain invariants and improve encapsulationUsing
Self::from_raw_parts(fq, len, vec![0; number_of_limbs])and thenew_with_capacitypath that resizes tofq.number(len)both respect thefrom_raw_partsinvariant (limbs.len() == fq.number(len)) and centralize initialization ininner.rs. Looks correct and in line with the encapsulation goal.
84-90:is_emptyandprimecorrectly leverage new accessorsUsing
self.len()inis_emptyandself.fq().characteristic().to_dyn()inprimeis a straightforward and correct migration away from direct field access.
92-102: Slice constructors correctly enforce bounds and use accessor‑based constructorsThe updated
slice/slice_mut:
- Assert
start <= end && end <= self.len(), guarding all index use.- Pass
self.fq()andself.limbs()/self.limbs_mut()intoFqSlice::new/FqSliceMut::new.This keeps invariants local to the type and is consistent with the
inner.rsaccessor design.
116-128: Field consistency checks now go throughfq()accessorThe assertions
assert_eq!(self.fq(), value.field());inadd_basis_elementandset_entryare semantically equivalent to the prior direct field checks and maintain the expected invariant that entries live in the same field as the vector.
138-143:set_to_zerocorrectly useslimbs_mutand preserves invariant about zero encodingLooping over
self.limbs_mut()and setting limbs to0matches the documented invariant thatfq.encode(fq.zero())is always zero, while keeping writes funneled through the accessor.
161-186:add_offsetcorrectly uses accessors and respects limb offset logicThe updated assertions (
self.fq(),other.fq(),self.len()) and the use ofself.limbs_mut()/other.limbs()(including passing them intoadd_simd) preserve the original semantics. Themin_limbcomputation and theskip(min_limb)zip over limbs still line up withoffset / entries_per_limb().
192-207:assignandassign_partialrefactor preserves invariants
assignnow usesself.limbs_mut().copy_from_slice(other.limbs()), which is a direct and safe replacement for copying the raw limb buffer after asserting same field and length.assign_partialcopies onlyother.limbs().len()limbs, then zeroes the remainder viaself.limbs_mut()[..].iter_mut(), correctly reusing existing capacity while ensuring trailing entries are zero.Both keep the vector’s limb layout consistent with the existing
len.
209-211:is_zeroimplementation is consistent with limb representationChecking
self.limbs().iter().all(|&x| x == 0)is an appropriate use of the new accessor and matches the representation invariant that zero vectors are all-zero limbs.
213-231:extend_lenandset_scratch_vector_sizemaintainlen/limbsconsistencyBoth methods:
- Use
self.len()/self.len_mut()instead of the raw field.- Compute
new_lenviaself.fq().number(len)andresizethe underlyingvec_mut()with zeros.This keeps the internal invariant
limbs.len() == fq.number(len)in sync after resizing. Mutatinglenbefore/afterresizeis fine here since the whole operation holds an exclusive&mut self.
248-257:trim_startuses accessors and preserves limb/length invariantThe method:
- Asserts
n <= self.len()and thatnis a multiple ofentries_per_limb().- Computes
num_limbs = n / entries_per.- Drains the first
num_limbslimbs fromself.vec_mut()and then subtractsnfrom*self.len_mut().Given
nis a multiple of entries per limb, the resultinglenand limb count remain consistent withfq.number(len).
259-276:sign_ruleaccessor migration is straightforwardSwapping direct field access for
self.fq()andself.limbs()/other.limbs()insign_rulekeeps the logic intact. The indices and loop bounds still align (0..self.limbs().len()with indexed access into both vectors).
278-286:add_truncatecorrectly uses accessors and preserves truncation semanticsUsing
self.fq()forfq,self.limbs_mut().iter_mut().zip_eq(other.limbs()), and thenfq.truncate(*left)?preserves the existing semantics while routing all buffer access through accessors. TheOption<()>behavior via?is unchanged.
339-368:first_nonzeroanddensitycorrectly usefq()and limb accessors
first_nonzeronow pullsentries_per_limb,bit_length, andbitmaskfromself.fq()and scansself.limbs(); the index computation and decode call are unchanged.densityusesself.fq().q()to branch between bit counting (forq == 2) anditer_nonzero().count(), then divides byself.len().Both functions are cleanly migrated to the accessor‑based API and preserve previous semantics.
443fedc to
fbd2dd9
Compare
This decouples the method implementations from the actual struct contents, which makes it easier to refactor in the near future.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.