-
Notifications
You must be signed in to change notification settings - Fork 39
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
Implement Range for OpStack #338
base: master
Are you sure you want to change the base?
Conversation
Thanks for taking this on! Unfortunately, there is a logic error in the implementation. The documentation of field /// The underlying, actual stack. When manually accessing, be aware of reversed indexing:
/// while `op_stack[0]` is the top of the stack, `op_stack.stack[0]` is the lowest element in
/// the stack.
Consequently, accessing |
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.
This file is only generated due to a bug in proptest
. It has purposefully not been commited.
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.
I have added processor.txt
into .gitignore
. Does it sound good to you ?
triton-isa/src/op_stack.rs
Outdated
@@ -4,6 +4,7 @@ use std::fmt::Result as FmtResult; | |||
use std::num::TryFromIntError; | |||
use std::ops::Index; | |||
use std::ops::IndexMut; | |||
use std::ops::{Range, RangeInclusive}; |
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.
In this repository, we use an import granularity of item
in order to trivialize git merging of imports.
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.
I have merged all std::ops
imports together: use std::ops::{Index, IndexMut, Range, RangeInclusive};
triton-isa/src/op_stack.rs
Outdated
if range.end <= self.stack.len() && range.start < range.end { | ||
&self.stack[range] | ||
} else { | ||
panic!("a range is out of bounds") |
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.
I think's it's cleaner to just use the underlying panics, should there be any:
- By not implementing these checks yourself, you cannot introduce logic bugs in them. Concretely, the range check in the
impl Index<RangeInclusive<usize>> for OpStack
looks suspiciously similar to the one inimpl Index<Range<usize>> for OpStack
. - The panic's message will correctly reflect the kind of access violation. For example, if the range is invalid, the current implementation will display an error regarding range bounds, whereas rust's panic message reads “slice index starts at
x
but ends aty
.” - By dropping the manual bounds-check here people will see the accustomed message “index out of bounds: the len is
x
but the index isy
.” - The stacktrace will point to the relevant method in either case.
@jan-ferdinand Sorry for delay. Thanks for your clarifications. Any thoughts on my update? |
fn compute_range(start: usize, end: usize, len: usize) -> (usize, usize) { | ||
match (start, end) { | ||
(0, end) if end == len => (0, len), | ||
(0, end) => (len - end, len), | ||
_ => (len - end, len - start), | ||
} | ||
} |
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.
The name does not really describe what the function does. A better name could be flip_range
, indicating that the range is mirrored or flipped along the “length” axis. There might be better names yet.
Furthermore, I don't think the special cases are necessary since they all collapse into the general case.
fn flip_range(start: usize, end: usize, len: usize) -> (usize, usize) {
(len - end, len - start)
}
@@ -2,8 +2,7 @@ use std::fmt::Display; | |||
use std::fmt::Formatter; | |||
use std::fmt::Result as FmtResult; | |||
use std::num::TryFromIntError; | |||
use std::ops::Index; | |||
use std::ops::IndexMut; | |||
use std::ops::{Index, IndexMut, Range, RangeInclusive}; |
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.
That's the wrong way around. 🙂 You've applied item granularity “module”, whereas granularity “item” results in one line per use
d type. See also here.
let (start, end) = compute_range(range.start, range.end, self.stack.len()); | ||
let mut stack = self.stack[start..end].to_vec(); | ||
stack.reverse(); | ||
Box::leak(Box::new(stack)) |
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.
That's a creative way of solving this. 😃 However, I feel uncomfortable with consciously introducing memory leaks.
The approach you take here indicates to me that implementing Index<Range<_>> for OpStack
requires refactoring OpStack
such that the internal field stack
is always in the correct order, for example using a VecDeque
. It's probably a good idea to make the field non-public at the same time. Please let me know if this is too much scope creep. 😌
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.
Introduction of VecDeque
sounds a good idea.
pub struct OpStack {
pub stack: VecDeque<BFieldElement>,
underflow_io_sequence: Vec<UnderflowIO>,
}
impl OpStack {
pub fn new(program_digest: Digest) -> Self {
let mut stack = VecDeque::with_capacity(OpStackElement::COUNT);
stack.extend(program_digest.values());
Self {
stack,
underflow_io_sequence: vec![],
}
}
In this case we do not reverse program_digest
and set self.stack[0]
is a top of the stack.
Additionally, we can update OpStack.push, OpStack.pop
to add new item to the beginning of the stack and remove item from the beginning of the stack, respectively. Vec<BFieldElement>
lacks this functionality.
pub fn push(&mut self, element: BFieldElement) {
self.stack.push_front(element);
self.record_underflow_io(UnderflowIO::Write);
}
pub fn pop(&mut self) -> Result<BFieldElement> {
self.record_underflow_io(UnderflowIO::Read);
self.stack.pop_front().ok_or(OpStackError::TooShallow)
}
pub fn insert(&mut self, index: OpStackElement, element: BFieldElement) {
let insertion_index = usize::from(index);
self.stack.insert(insertion_index, element);
self.record_underflow_io(UnderflowIO::Write);
}
Index<Range<usize>> for OpStack
can be implemented like that :
impl Index<Range<usize>> for OpStack {
type Output = [BFieldElement];
fn index(&self, range: Range<usize>) -> &Self::Output {
&self.stack.as_slices().0[range.start..range.end]
}
}
I wonder if this make sense.
Closes #322