Transaction

0F189F1AFDF632…FEF319B3DB77

Block 25,948 · index 1 · indexed

Summary

Hash
0F189F1AFDF63278354174E5BC70F537D1521075C37D964F12A6FEF319B3DB77
Block
25,948
Size
5727 bytes
Gas used
10,031,578 / 12,037,953
Fee
1000000ugnot
Memo
gnopublish
Status
success

Messages

Arguments · 9

  1. #1ringbuffer
  2. #2README.md
  3. #3# `gno.land/p/moul/x/daily/ringbuffer/v0` **Fixed-capacity FIFO that overwrites its oldest entry** — `New`, `Push`, `Pop`, `Peek`, `At`, `Slice`, `Reset`, `Len`, `Cap`, `Full`, `Empty`, `MaxCap`. The bounded cousin of a queue, and the bound is the point: on chain an unbounded queue is an unbounded storage bill, whereas a ring buffer's cost is decided once, at construction. The right shape for "last N events", "recent messages", or any rolling window. ```go import "gno.land/p/moul/x/daily/ringbuffer/v0" r := ringbuffer.New(3) r.Push("a"); r.Push("b"); r.Push("c") evicted, dropped := r.Push("d") // "a", true r.Slice() // ["b" "c" "d"] ``` `Push` **returns what it evicted**, so a rolling window never loses data silently — the one thing this shape must not do. Backed by a flat slice with head/length indices: no per-element allocation, no shifting on `Pop`. `Pop` and `Reset` clear the vacated slots so no reference is pinned after it is logically gone. A zero-capacity buffer stores nothing and says so (`Push` hands the value straight back), and is never reported as `Full` — a buffer that holds nothing cannot be full. **Live demo:** [`r/moul/x/daily/ringbufferdemo`](https://github.com/moul/gno-contracts/tree/main/r/moul/x/daily/ringbufferdemo/v0) · render it at [`/r/moul/x/daily/ringbufferdemo/v0`](https://gno.land/r/moul/x/daily/ringbufferdemo/v0). <!-- BEGIN GNOCONTRACTS FOOTER (generated by `make readmes`; do not edit below) --> --- Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage. > 🧪 **Highly experimental — potentially vibe-coded.** Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md). <!-- END GNOCONTRACTS FOOTER -->
  4. #4gnomod.toml
  5. #5module = "gno.land/p/moul/x/daily/ringbuffer/v0" gno = "0.9"
  6. #6ringbuffer.gno
  7. #7// Package ringbuffer is a fixed-capacity FIFO that overwrites its oldest entry // when full, as a pure, reusable package. // // This is the bounded cousin of an unbounded queue, and the bound is the point: // on chain an unbounded queue is an unbounded storage bill, whereas a ring // buffer's cost is decided once, at construction. It is the right shape for // "last N events", "recent messages", or any rolling window. // // Backed by a flat slice with head/length indices — no per-element allocation, // no shifting on Pop. // // A live demo of this package is at // [r/moul/x/daily/ringbufferdemo](/r/moul/x/daily/ringbufferdemo/v0). package ringbuffer // MaxCap bounds a buffer so allocation stays predictable. const MaxCap = 4096 // RingBuffer is a fixed-capacity FIFO of strings. type RingBuffer struct { buf []string head int // index of the oldest element n int // number of live elements } // New returns an empty buffer of the given capacity, clamped to [0, MaxCap]. func New(capacity int) *RingBuffer { if capacity < 0 { capacity = 0 } if capacity > MaxCap { capacity = MaxCap } return &RingBuffer{buf: make([]string, capacity)} } // Cap returns the capacity. func (r *RingBuffer) Cap() int { return len(r.buf) } // Len returns how many elements are live. func (r *RingBuffer) Len() int { return r.n } // Full reports whether the next Push will overwrite. func (r *RingBuffer) Full() bool { return len(r.buf) > 0 && r.n == len(r.buf) } // Empty reports whether there is nothing to read. func (r *RingBuffer) Empty() bool { return r.n == 0 } // Push appends v. When the buffer is full the OLDEST element is dropped to make // room, and that element is returned with dropped=true — losing data silently // is the one thing a rolling window must not do. // // A zero-capacity buffer accepts nothing and reports the value straight back. func (r *RingBuffer) Push(v string) (evicted string, dropped bool) { if len(r.buf) == 0 { return v, true } if r.n == len(r.buf) { evicted = r.buf[r.head] r.buf[r.head] = v r.head = (r.head + 1) % len(r.buf) return evicted, true } r.buf[(r.head+r.n)%len(r.buf)] = v r.n++ return "", false } // Pop removes and returns the oldest element; ok is false when empty. func (r *RingBuffer) Pop() (v string, ok bool) { if r.n == 0 { return "", false } v = r.buf[r.head] r.buf[r.head] = "" // release the reference; don't pin a string we no longer own r.head = (r.head + 1) % len(r.buf) r.n-- return v, true } // Peek returns the oldest element without removing it. func (r *RingBuffer) Peek() (v string, ok bool) { if r.n == 0 { return "", false } return r.buf[r.head], true } // At returns the i-th element counting from the oldest (0 = oldest). func (r *RingBuffer) At(i int) (v string, ok bool) { if i < 0 || i >= r.n { return "", false } return r.buf[(r.head+i)%len(r.buf)], true } // Slice returns the live elements oldest-first, as an independent copy. func (r *RingBuffer) Slice() []string { out := make([]string, 0, r.n) for i := 0; i < r.n; i++ { out = append(out, r.buf[(r.head+i)%len(r.buf)]) } return out } // Reset empties the buffer, releasing every stored reference. func (r *RingBuffer) Reset() { for i := range r.buf { r.buf[i] = "" } r.head, r.n = 0, 0 }
  8. #8/gno.MemPackageType
  9. #9 MPUserProd

Result log

msg:0,success:true,log:,events:[]

← Back to block 25,948