Skip to content
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

fix: Validate Circular Buffer Capacity #91

Merged
merged 8 commits into from
Nov 26, 2023
41 changes: 34 additions & 7 deletions exercises/practice/circular-buffer/.meta/proof.ci.wat
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
(module
(memory 1)
(memory 1 1)
(global $head (mut i32) (i32.const -1))
(global $tail (mut i32) (i32.const -1))
(global $capacity (mut i32) (i32.const 0))
(global $i32Size i32 (i32.const 4))


;; capacity: the number of elements to store
;; elementSize: the size of the element to store in bytes
;; Does not support resizing circular buffer. Wipes all data
;;
;; Initialize a circular buffer of i32s with a given capacity
;;
;; @param {i32} newCapacity - capacity of the circular buffer between 0 and 16,384
;; in order to fit in a single 64KiB WebAssembly page
;;
;; @returns {i32} 0 on success or -1 on error
;;
(func (export "init") (param $newCapacity i32) (result i32)
;; a WebAssembly page is 4096 bytes, so up to 1024 i32s
bushidocodes marked this conversation as resolved.
Show resolved Hide resolved
(if (i32.gt_s (local.get $newCapacity) (i32.const 1024)) (then
(if (i32.gt_s (local.get $newCapacity) (i32.const 16384)) (then
(return (i32.const -1))
))

Expand All @@ -21,11 +25,21 @@
(i32.const 0)
)

;;
;; Clear the circular buffer
;;
(func (export "clear")
(global.set $head (i32.const -1))
(global.set $tail (i32.const -1))
)

;;
;; Add an element to the circular buffer
;;
;; @param {i32} elem - element to add to the circular buffer
;;
;; @returns {i32} 0 on success or -1 if full
;;
(func (export "write") (param $elem i32) (result i32)
(local $temp i32)
;; Table has capacity of zero
Expand All @@ -52,6 +66,14 @@
(i32.const 0)
)

;;
;; Add an element to the circular buffer, overwriting the oldest element
;; if the buffer is full
;;
;; @param {i32} elem - element to add to the circular buffer
;;
;; @returns {i32} 0 on success or -1 if full (capacity of zero)
;;
(func (export "forceWrite") (param $elem i32) (result i32)
(local $temp i32)
;; Table has capacity of zero
Expand All @@ -78,7 +100,12 @@
(i32.const 0)
)

;; Go-style error handling type (i32,i32)
;;
;; Read the oldest element from the circular buffer, if not empty
;;
;; @returns {i32} element on success or -1 if empty
;; @returns {i32} status code set to 0 on success or -1 if empty
;;
(func (export "read") (result i32 i32)
(local $result i32)

Expand Down
11 changes: 11 additions & 0 deletions exercises/practice/circular-buffer/circular-buffer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,15 @@ describe("CircularBuffer", () => {
expect(currentInstance.exports.read()).toEqual([4, 0]);
expect(currentInstance.exports.read()).toEqual([-1, -1]);
});

xtest("Should be able to grow up to a full 64KiB page", () => {
expect(currentInstance.exports.init(16384)).toEqual(0);
for (let i = 0; i < 16384; i++) {
expect(currentInstance.exports.write(i)).toEqual(0);
}
});

xtest("init should fail if greater than full 64KiB page", () => {
expect(currentInstance.exports.init(16385)).toEqual(-1);
});
});
7 changes: 4 additions & 3 deletions exercises/practice/circular-buffer/circular-buffer.wat
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
(module
(memory 1)
;; Cap the memory at a single 64KiB page
(memory 1 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: is this (capping the memory) something we want to do for other exercises too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can! It's mostly useful for preventing errors in loops that call memory.grow, so it's particularly important here. No harm in doing this more widely though. I'll do that as a follow-up PR.

;; Add globals here!

;;
;; Initialize a circular buffer of i32s with a given capacity
;;
;; @param {i32} newCapacity - capacity of the circular buffer between 0 and 1024
;; in order to fit in a single WebAssembly page
;; @param {i32} newCapacity - capacity of the circular buffer between 0 and 16,384
;; in order to fit in a single 64KiB WebAssembly page
;;
;; @returns {i32} 0 on success or -1 on error
;;
Expand Down
Loading