mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-15 18:12:47 +03:00
Merge pull request #783 from urbit/ford-turbo-clock
Improvements to +clock / +capped-queue
This commit is contained in:
commit
e3e5ae6d30
@ -126,6 +126,7 @@
|
||||
::
|
||||
|= pit=vase
|
||||
::
|
||||
=, contain
|
||||
=, ford
|
||||
:: ford internal data structures
|
||||
::
|
||||
@ -184,216 +185,6 @@
|
||||
care-paths=(set [care=care:clay =path])
|
||||
== == == ==
|
||||
--
|
||||
::
|
||||
|%
|
||||
:: +clock: polymorphic cache type for use with the clock replacement algorithm
|
||||
::
|
||||
:: The +by-clock core wraps interface arms for manipulating a mapping from
|
||||
:: :key-type to :val-type. Detailed docs for this type can be found there.
|
||||
::
|
||||
++ clock
|
||||
|* $: :: key-type: mold of keys
|
||||
::
|
||||
key-type=mold
|
||||
:: val-type: mold of values
|
||||
::
|
||||
val-type=mold
|
||||
==
|
||||
$: lookup=(map key-type [val=val-type fresh=@ud])
|
||||
queue=(qeu key-type)
|
||||
size=@ud
|
||||
max-size=_2.048
|
||||
depth=_1
|
||||
==
|
||||
:: +capped-queue: a +qeu with a maximum number of entries
|
||||
::
|
||||
++ capped-queue
|
||||
|* item-type=mold
|
||||
$: queue=(qeu item-type)
|
||||
size=@ud
|
||||
max-size=_64
|
||||
==
|
||||
--
|
||||
|%
|
||||
:: +by-clock: interface core for a cache using the clock replacement algorithm
|
||||
::
|
||||
:: Presents an interface for a mapping, but somewhat specialized, and with
|
||||
:: stateful accessors. The clock's :depth parameter is used as the maximum
|
||||
:: freshness that an entry can have. The standard clock algorithm has a depth
|
||||
:: of 1, meaning that a single sweep of the arm will delete the entry. For
|
||||
:: more scan resistance, :depth can be set to a higher number.
|
||||
::
|
||||
:: Internally, :clock maintains a :lookup of type
|
||||
:: `(map key-type [val=val-type fresh=@ud])`, where :depth.clock is the
|
||||
:: maximum value of :fresh. Looking up a key increments its freshness, and a
|
||||
:: sweep of the clock arm decrements its freshness.
|
||||
::
|
||||
:: The clock arm is stored as :queue, which is a `(qeu key-type)`. The head
|
||||
:: of the queue represents the position of the clock arm. New entries are
|
||||
:: inserted at the tail of the queue. When the clock arm sweeps, it
|
||||
:: pops the head off the queue. If the :fresh of the head's entry in :lookup
|
||||
:: is 0, remove the entry from the mapping and replace it with the new entry.
|
||||
:: Otherwise, decrement the entry's freshness, put it back at the tail of
|
||||
:: the queue, and pop the next head off the queue and try again.
|
||||
::
|
||||
:: Cache entries must be immutable: a key cannot be overwritten with a new
|
||||
:: value. This property is enforced for entries currently stored in the
|
||||
:: cache, but it is not enforced for previously deleted entries, since we
|
||||
:: no longer remember what that key's value was supposed to be.
|
||||
::
|
||||
++ by-clock
|
||||
|* [key-type=mold val-type=mold]
|
||||
|_ clock=(clock key-type val-type)
|
||||
:: +get: looks up a key, marking it as fresh
|
||||
::
|
||||
++ get
|
||||
|= key=key-type
|
||||
^- [(unit val-type) _clock]
|
||||
::
|
||||
=+ maybe-got=(~(get by lookup.clock) key)
|
||||
?~ maybe-got
|
||||
[~ clock]
|
||||
::
|
||||
=. clock (freshen key)
|
||||
::
|
||||
[`val.u.maybe-got clock]
|
||||
:: +put: add a new cache entry, possibly removing an old one
|
||||
::
|
||||
++ put
|
||||
|= [key=key-type val=val-type]
|
||||
^+ clock
|
||||
:: no overwrite allowed, but allow duplicate puts
|
||||
::
|
||||
?^ existing=(~(get by lookup.clock) key)
|
||||
:: val must not change
|
||||
::
|
||||
?> =(val val.u.existing)
|
||||
::
|
||||
(freshen key)
|
||||
::
|
||||
=? clock =(max-size.clock +(size.clock))
|
||||
evict
|
||||
::
|
||||
%_ clock
|
||||
size +(size.clock)
|
||||
lookup (~(put by lookup.clock) key [val 1])
|
||||
queue (~(put to queue.clock) key)
|
||||
==
|
||||
:: +freshen: increment the protection level on an entry
|
||||
::
|
||||
++ freshen
|
||||
|= key=key-type
|
||||
^+ clock
|
||||
%_ clock
|
||||
lookup
|
||||
%+ ~(jab by lookup.clock) key
|
||||
|= entry=[val=val-type fresh=@ud]
|
||||
entry(fresh (max +(fresh.entry) depth.clock))
|
||||
==
|
||||
:: +resize: changes the maximum size, removing entries if needed
|
||||
::
|
||||
++ resize
|
||||
|= new-max=@ud
|
||||
^+ clock
|
||||
::
|
||||
=. max-size.clock new-max
|
||||
::
|
||||
?: (gte new-max size.clock)
|
||||
clock
|
||||
::
|
||||
(trim (sub size.clock new-max))
|
||||
:: +evict: remove an entry from the cache
|
||||
::
|
||||
++ evict
|
||||
^+ clock
|
||||
::
|
||||
=. size.clock (dec size.clock)
|
||||
::
|
||||
|-
|
||||
^+ clock
|
||||
::
|
||||
=^ old-key queue.clock ~(get to queue.clock)
|
||||
=/ old-entry (~(got by lookup.clock) old-key)
|
||||
::
|
||||
?: =(0 fresh.old-entry)
|
||||
clock(lookup (~(del by lookup.clock) old-key))
|
||||
::
|
||||
%_ $
|
||||
lookup.clock
|
||||
(~(put by lookup.clock) old-key old-entry(fresh (dec fresh.old-entry)))
|
||||
::
|
||||
queue.clock
|
||||
(~(put to queue.clock) old-key)
|
||||
==
|
||||
:: +trim: remove :count entries from the cache
|
||||
::
|
||||
++ trim
|
||||
|= count=@ud
|
||||
^+ clock
|
||||
?: =(0 count)
|
||||
clock
|
||||
$(count (dec count), clock evict)
|
||||
:: +purge: removes all cache entries
|
||||
::
|
||||
++ purge
|
||||
^+ clock
|
||||
%_ clock
|
||||
lookup ~
|
||||
queue ~
|
||||
size 0
|
||||
==
|
||||
--
|
||||
:: +to-capped-queue: interface door for +capped-queue
|
||||
::
|
||||
++ to-capped-queue
|
||||
|* item-type=mold
|
||||
|_ queue=(capped-queue item-type)
|
||||
:: +put: enqueue :item, possibly popping and producing an old item
|
||||
::
|
||||
++ put
|
||||
|= item=item-type
|
||||
^- [(unit item-type) _queue]
|
||||
:: are we already at max capacity?
|
||||
::
|
||||
?. =(size.queue max-size.queue)
|
||||
:: we're below max capacity, so push and increment size
|
||||
::
|
||||
=. queue.queue (~(put to queue.queue) item)
|
||||
=. size.queue +(size.queue)
|
||||
::
|
||||
[~ queue]
|
||||
:: we're at max capacity, so pop before pushing; size is unchanged
|
||||
::
|
||||
=^ oldest queue.queue ~(get to queue.queue)
|
||||
=. queue.queue (~(put to queue.queue) item)
|
||||
::
|
||||
[`oldest queue]
|
||||
:: +get: pop an item off the queue, adjusting size
|
||||
::
|
||||
++ get
|
||||
^- [item-type _queue]
|
||||
::
|
||||
=. size.queue (dec size.queue)
|
||||
=^ oldest queue.queue ~(get to queue.queue)
|
||||
::
|
||||
[oldest queue]
|
||||
:: change the :max-size of the queue, popping items if necessary
|
||||
::
|
||||
++ resize
|
||||
=| pops=(list item-type)
|
||||
|= new-max=@ud
|
||||
^+ [pops queue]
|
||||
:: we're not overfull, so no need to pop off more items
|
||||
::
|
||||
?: (gte new-max size.queue)
|
||||
[(flop pops) queue(max-size new-max)]
|
||||
:: we're above capacity; pop an item off and recurse
|
||||
::
|
||||
=^ oldest queue get
|
||||
::
|
||||
$(pops [oldest pops])
|
||||
--
|
||||
--
|
||||
|%
|
||||
:: +axle: overall ford state
|
||||
::
|
||||
@ -6084,7 +5875,9 @@
|
||||
?: ?=(%pin -.schematic.build)
|
||||
~
|
||||
::
|
||||
=/ subs ~(tap in ~(key by subs:(~(got by builds.state) build)))
|
||||
=/ subs
|
||||
~| [%collect-live-resource (build-to-tape build)]
|
||||
~(tap in ~(key by subs:(~(got by builds.state) build)))
|
||||
=| resources=(jug disc resource)
|
||||
|-
|
||||
?~ subs
|
||||
|
219
sys/zuse.hoon
219
sys/zuse.hoon
@ -40,6 +40,34 @@
|
||||
:: miscellaneous systems types
|
||||
::+|
|
||||
++ ares (unit {p/term q/(list tank)}) :: possible error
|
||||
:: +capped-queue: a +qeu with a maximum number of entries
|
||||
::
|
||||
++ capped-queue
|
||||
|* item-type=mold
|
||||
$: queue=(qeu item-type)
|
||||
size=@ud
|
||||
max-size=_64
|
||||
==
|
||||
:: +clock: polymorphic cache type for use with the clock replacement algorithm
|
||||
::
|
||||
:: The +by-clock core wraps interface arms for manipulating a mapping from
|
||||
:: :key-type to :val-type. Detailed docs for this type can be found there.
|
||||
::
|
||||
++ clock
|
||||
|* $: :: key-type: mold of keys
|
||||
::
|
||||
key-type=mold
|
||||
:: val-type: mold of values
|
||||
::
|
||||
val-type=mold
|
||||
==
|
||||
$: lookup=(map key-type [val=val-type fresh=@ud])
|
||||
queue=(qeu key-type)
|
||||
size=@ud
|
||||
max-size=_2.048
|
||||
depth=_1
|
||||
==
|
||||
::
|
||||
++ coop (unit ares) :: possible error
|
||||
++ json :: normal json value
|
||||
$@ ~ :: null
|
||||
@ -5675,6 +5703,197 @@
|
||||
^- @tas
|
||||
?~(une %no (mill u.une))
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
++ contain ^?
|
||||
|%
|
||||
:: +by-clock: interface core for a cache using the clock replacement algorithm
|
||||
::
|
||||
:: Presents an interface for a mapping, but somewhat specialized, and with
|
||||
:: stateful accessors. The clock's :depth parameter is used as the maximum
|
||||
:: freshness that an entry can have. The standard clock algorithm has a depth
|
||||
:: of 1, meaning that a single sweep of the arm will delete the entry. For
|
||||
:: more scan resistance, :depth can be set to a higher number.
|
||||
::
|
||||
:: Internally, :clock maintains a :lookup of type
|
||||
:: `(map key-type [val=val-type fresh=@ud])`, where :depth.clock is the
|
||||
:: maximum value of :fresh. Looking up a key increments its freshness, and a
|
||||
:: sweep of the clock arm decrements its freshness.
|
||||
::
|
||||
:: The clock arm is stored as :queue, which is a `(qeu key-type)`. The head
|
||||
:: of the queue represents the position of the clock arm. New entries are
|
||||
:: inserted at the tail of the queue. When the clock arm sweeps, it
|
||||
:: pops the head off the queue. If the :fresh of the head's entry in :lookup
|
||||
:: is 0, remove the entry from the mapping and replace it with the new entry.
|
||||
:: Otherwise, decrement the entry's freshness, put it back at the tail of
|
||||
:: the queue, and pop the next head off the queue and try again.
|
||||
::
|
||||
:: Cache entries must be immutable: a key cannot be overwritten with a new
|
||||
:: value. This property is enforced for entries currently stored in the
|
||||
:: cache, but it is not enforced for previously deleted entries, since we
|
||||
:: no longer remember what that key's value was supposed to be.
|
||||
::
|
||||
++ by-clock
|
||||
|* [key-type=mold val-type=mold]
|
||||
|_ clock=(clock key-type val-type)
|
||||
:: +get: looks up a key, marking it as fresh
|
||||
::
|
||||
++ get
|
||||
|= key=key-type
|
||||
^- [(unit val-type) _clock]
|
||||
::
|
||||
=+ maybe-got=(~(get by lookup.clock) key)
|
||||
?~ maybe-got
|
||||
[~ clock]
|
||||
::
|
||||
=. clock (freshen key)
|
||||
::
|
||||
[`val.u.maybe-got clock]
|
||||
:: +put: add a new cache entry, possibly removing an old one
|
||||
::
|
||||
++ put
|
||||
|= [key=key-type val=val-type]
|
||||
^+ clock
|
||||
:: do nothing if our size is 0 so we don't decrement-underflow
|
||||
::
|
||||
?: =(0 max-size.clock)
|
||||
clock
|
||||
:: no overwrite allowed, but allow duplicate puts
|
||||
::
|
||||
?^ existing=(~(get by lookup.clock) key)
|
||||
:: val must not change
|
||||
::
|
||||
?> =(val val.u.existing)
|
||||
::
|
||||
(freshen key)
|
||||
::
|
||||
=? clock =(max-size.clock size.clock)
|
||||
evict
|
||||
::
|
||||
%_ clock
|
||||
size +(size.clock)
|
||||
lookup (~(put by lookup.clock) key [val 1])
|
||||
queue (~(put to queue.clock) key)
|
||||
==
|
||||
:: +freshen: increment the protection level on an entry
|
||||
::
|
||||
++ freshen
|
||||
|= key=key-type
|
||||
^+ clock
|
||||
%_ clock
|
||||
lookup
|
||||
%+ ~(jab by lookup.clock) key
|
||||
|= entry=[val=val-type fresh=@ud]
|
||||
entry(fresh (min +(fresh.entry) depth.clock))
|
||||
==
|
||||
:: +resize: changes the maximum size, removing entries if needed
|
||||
::
|
||||
++ resize
|
||||
|= new-max=@ud
|
||||
^+ clock
|
||||
::
|
||||
=. max-size.clock new-max
|
||||
::
|
||||
?: (gte new-max size.clock)
|
||||
clock
|
||||
::
|
||||
(trim (sub size.clock new-max))
|
||||
:: +evict: remove an entry from the cache
|
||||
::
|
||||
++ evict
|
||||
^+ clock
|
||||
::
|
||||
=. size.clock (dec size.clock)
|
||||
::
|
||||
|-
|
||||
^+ clock
|
||||
::
|
||||
=^ old-key queue.clock ~(get to queue.clock)
|
||||
=/ old-entry (~(got by lookup.clock) old-key)
|
||||
::
|
||||
?: =(0 fresh.old-entry)
|
||||
clock(lookup (~(del by lookup.clock) old-key))
|
||||
::
|
||||
%_ $
|
||||
lookup.clock
|
||||
(~(put by lookup.clock) old-key old-entry(fresh (dec fresh.old-entry)))
|
||||
::
|
||||
queue.clock
|
||||
(~(put to queue.clock) old-key)
|
||||
==
|
||||
:: +trim: remove :count entries from the cache
|
||||
::
|
||||
++ trim
|
||||
|= count=@ud
|
||||
^+ clock
|
||||
?: =(0 count)
|
||||
clock
|
||||
$(count (dec count), clock evict)
|
||||
:: +purge: removes all cache entries
|
||||
::
|
||||
++ purge
|
||||
^+ clock
|
||||
%_ clock
|
||||
lookup ~
|
||||
queue ~
|
||||
size 0
|
||||
==
|
||||
--
|
||||
:: +to-capped-queue: interface door for +capped-queue
|
||||
::
|
||||
:: Provides a queue of a limited size where pushing additional items will
|
||||
:: force pop the items at the front of the queue.
|
||||
::
|
||||
++ to-capped-queue
|
||||
|* item-type=mold
|
||||
|_ queue=(capped-queue item-type)
|
||||
:: +put: enqueue :item, possibly popping and producing an old item
|
||||
::
|
||||
++ put
|
||||
|= item=item-type
|
||||
^- [(unit item-type) _queue]
|
||||
:: are we already at max capacity?
|
||||
::
|
||||
?. =(size.queue max-size.queue)
|
||||
:: we're below max capacity, so push and increment size
|
||||
::
|
||||
=. queue.queue (~(put to queue.queue) item)
|
||||
=. size.queue +(size.queue)
|
||||
::
|
||||
[~ queue]
|
||||
:: we're at max capacity, so pop before pushing; size is unchanged
|
||||
::
|
||||
=^ oldest queue.queue ~(get to queue.queue)
|
||||
=. queue.queue (~(put to queue.queue) item)
|
||||
::
|
||||
[`oldest queue]
|
||||
:: +get: pop an item off the queue, adjusting size
|
||||
::
|
||||
++ get
|
||||
^- [item-type _queue]
|
||||
::
|
||||
=. size.queue (dec size.queue)
|
||||
=^ oldest queue.queue ~(get to queue.queue)
|
||||
::
|
||||
[oldest queue]
|
||||
:: change the :max-size of the queue, popping items if necessary
|
||||
::
|
||||
++ resize
|
||||
=| pops=(list item-type)
|
||||
|= new-max=@ud
|
||||
^+ [pops queue]
|
||||
:: we're not overfull, so no need to pop off more items
|
||||
::
|
||||
?: (gte new-max size.queue)
|
||||
[(flop pops) queue(max-size new-max)]
|
||||
:: we're above capacity; pop an item off and recurse
|
||||
::
|
||||
=^ oldest queue get
|
||||
::
|
||||
$(pops [oldest pops])
|
||||
--
|
||||
--
|
||||
:: ::
|
||||
:::: ++userlib :: (2u) non-vane utils
|
||||
:: ::::
|
||||
|
128
tests/sys/zuse/contain/capped-queue.hoon
Normal file
128
tests/sys/zuse/contain/capped-queue.hoon
Normal file
@ -0,0 +1,128 @@
|
||||
/+ tester
|
||||
::
|
||||
=, contain
|
||||
::
|
||||
|_ _tester:tester
|
||||
++ test-basic-capped-queue
|
||||
::
|
||||
=| q=(capped-queue @u)
|
||||
=. max-size.q 3
|
||||
:: specialize type
|
||||
::
|
||||
=+ to-capped-queue=(to-capped-queue @u)
|
||||
:: push a single element
|
||||
::
|
||||
=^ maybe1 q (~(put to-capped-queue q) 5)
|
||||
::
|
||||
=/ results1
|
||||
%- expect-eq !>
|
||||
:- ~
|
||||
maybe1
|
||||
=/ results2
|
||||
%- expect-eq !>
|
||||
:- 1
|
||||
size.q
|
||||
=/ results3
|
||||
%- expect-eq !>
|
||||
:- [~ 5]
|
||||
~(top to queue.q)
|
||||
:: remove the single element
|
||||
::
|
||||
=^ maybe2 q ~(get to-capped-queue q)
|
||||
::
|
||||
=/ results4
|
||||
%- expect-eq !>
|
||||
:- 5
|
||||
maybe2
|
||||
=/ results5
|
||||
%- expect-eq !>
|
||||
:- 0
|
||||
size.q
|
||||
::
|
||||
;: weld
|
||||
results1
|
||||
results2
|
||||
results3
|
||||
results4
|
||||
results5
|
||||
==
|
||||
::
|
||||
++ test-put-returns-evicted-value
|
||||
::
|
||||
=| q=(capped-queue @u)
|
||||
=. max-size.q 2
|
||||
:: specialize type
|
||||
::
|
||||
=+ to-capped-queue=(to-capped-queue @u)
|
||||
:: push enough values to evict one
|
||||
::
|
||||
=^ maybe1 q (~(put to-capped-queue q) 5)
|
||||
=/ results1
|
||||
%- expect-eq !>
|
||||
:- ~
|
||||
maybe1
|
||||
=/ results2
|
||||
%- expect-eq !>
|
||||
:- 1
|
||||
size.q
|
||||
::
|
||||
=^ maybe2 q (~(put to-capped-queue q) 6)
|
||||
=/ results3
|
||||
%- expect-eq !>
|
||||
:- ~
|
||||
maybe2
|
||||
=/ results4
|
||||
%- expect-eq !>
|
||||
:- 2
|
||||
size.q
|
||||
::
|
||||
=^ maybe3 q (~(put to-capped-queue q) 7)
|
||||
=/ results5
|
||||
%- expect-eq !>
|
||||
:- [~ 5]
|
||||
maybe3
|
||||
=/ results6
|
||||
%- expect-eq !>
|
||||
:- 2
|
||||
size.q
|
||||
::
|
||||
;: weld
|
||||
results1
|
||||
results2
|
||||
results3
|
||||
results4
|
||||
results5
|
||||
results6
|
||||
==
|
||||
::
|
||||
++ test-resize-evicts-on-shrink
|
||||
::
|
||||
=| q=(capped-queue @u)
|
||||
=. max-size.q 5
|
||||
:: specialize type
|
||||
::
|
||||
=+ to-capped-queue=(to-capped-queue @u)
|
||||
::
|
||||
=^ maybe1 q (~(put to-capped-queue q) 1)
|
||||
=^ maybe2 q (~(put to-capped-queue q) 2)
|
||||
=^ maybe3 q (~(put to-capped-queue q) 3)
|
||||
=^ maybe4 q (~(put to-capped-queue q) 4)
|
||||
=^ maybe5 q (~(put to-capped-queue q) 5)
|
||||
:: resize the size to 3; this should pop two items
|
||||
::
|
||||
=^ pops q (~(resize to-capped-queue q) 3)
|
||||
::
|
||||
=/ results1
|
||||
%- expect-eq !>
|
||||
:- [1 2 ~]
|
||||
pops
|
||||
=/ results2
|
||||
%- expect-eq !>
|
||||
:- 3
|
||||
size.q
|
||||
::
|
||||
;: weld
|
||||
results1
|
||||
results2
|
||||
==
|
||||
--
|
182
tests/sys/zuse/contain/clock.hoon
Normal file
182
tests/sys/zuse/contain/clock.hoon
Normal file
@ -0,0 +1,182 @@
|
||||
/+ tester
|
||||
::
|
||||
=, contain
|
||||
::
|
||||
|_ _tester:tester
|
||||
++ test-basic-clock
|
||||
::
|
||||
=| c=(clock @u tape)
|
||||
:: make max-size reasonable for testing
|
||||
::
|
||||
=. max-size.c 3
|
||||
:: specialize type
|
||||
::
|
||||
=+ by-clock=(by-clock @u tape)
|
||||
:: ensure we get a single key we put in
|
||||
::
|
||||
=. c (~(put by-clock c) 1 "one")
|
||||
=^ maybe1 c (~(get by-clock c) 1)
|
||||
=/ results1
|
||||
%- expect-eq !>
|
||||
:- [~ "one"]
|
||||
maybe1
|
||||
::
|
||||
=/ results2
|
||||
%- expect-eq !>
|
||||
:- 1
|
||||
size.c
|
||||
:: push that key out of the cache
|
||||
::
|
||||
=. c (~(put by-clock c) 2 "two")
|
||||
=. c (~(put by-clock c) 3 "three")
|
||||
=. c (~(put by-clock c) 4 "four")
|
||||
::
|
||||
=/ results3
|
||||
%- expect-eq !>
|
||||
:- 3
|
||||
size.c
|
||||
::
|
||||
=^ maybe2 c (~(get by-clock c) 1)
|
||||
=/ results4
|
||||
%- expect-eq !>
|
||||
:- ~
|
||||
maybe2
|
||||
::
|
||||
;: weld
|
||||
results1
|
||||
results2
|
||||
results3
|
||||
results4
|
||||
==
|
||||
::
|
||||
++ test-clock-purge
|
||||
::
|
||||
=| c=(clock @u tape)
|
||||
:: make max-size reasonable for testing
|
||||
::
|
||||
=. max-size.c 3
|
||||
:: specialize type
|
||||
::
|
||||
=+ by-clock=(by-clock @u tape)
|
||||
:: fill the clock
|
||||
::
|
||||
=. c (~(put by-clock c) 1 "one")
|
||||
=. c (~(put by-clock c) 2 "two")
|
||||
=. c (~(put by-clock c) 3 "three")
|
||||
:: purge the entire clock
|
||||
::
|
||||
=. c ~(purge by-clock c)
|
||||
::
|
||||
;: weld
|
||||
%- expect-eq !>
|
||||
:- 0
|
||||
size.c
|
||||
::
|
||||
%- expect-eq !>
|
||||
:- 3
|
||||
max-size.c
|
||||
::
|
||||
%- expect-eq !>
|
||||
:- ~
|
||||
lookup.c
|
||||
::
|
||||
%- expect-eq !>
|
||||
:- ~
|
||||
queue.c
|
||||
==
|
||||
::
|
||||
++ test-clock-trim
|
||||
::
|
||||
=| c=(clock @u tape)
|
||||
:: make max-size reasonable for testing
|
||||
::
|
||||
=. max-size.c 3
|
||||
:: specialize type
|
||||
::
|
||||
=+ by-clock=(by-clock @u tape)
|
||||
:: fill the clock
|
||||
::
|
||||
=. c (~(put by-clock c) 1 "one")
|
||||
=. c (~(put by-clock c) 2 "two")
|
||||
=. c (~(put by-clock c) 3 "three")
|
||||
:: trim 2/3 of the clock
|
||||
::
|
||||
=. c (~(trim by-clock c) 2)
|
||||
::
|
||||
;: weld
|
||||
%- expect-eq !>
|
||||
:- 1
|
||||
size.c
|
||||
::
|
||||
=^ results1 c (~(get by-clock c) 3)
|
||||
%- expect-eq !>
|
||||
:- [~ "three"]
|
||||
results1
|
||||
::
|
||||
%- expect-eq !>
|
||||
:- 1
|
||||
~(wyt by lookup.c)
|
||||
==
|
||||
::
|
||||
++ test-clock-resized-to-zero
|
||||
::
|
||||
=| c=(clock @u tape)
|
||||
:: make max-size reasonable for testing
|
||||
::
|
||||
=. max-size.c 3
|
||||
:: specialize type
|
||||
::
|
||||
=+ by-clock=(by-clock @u tape)
|
||||
:: fill the clock
|
||||
::
|
||||
=. c (~(put by-clock c) 1 "one")
|
||||
=. c (~(put by-clock c) 2 "two")
|
||||
=. c (~(put by-clock c) 3 "three")
|
||||
:: resize the clock so it has zero elements
|
||||
::
|
||||
=. c (~(resize by-clock c) 0)
|
||||
::
|
||||
=/ results1
|
||||
%- expect-eq !>
|
||||
:- 0
|
||||
size.c
|
||||
::
|
||||
=/ results2
|
||||
%- expect-eq !>
|
||||
:- ~
|
||||
lookup.c
|
||||
::
|
||||
=/ results3
|
||||
%- expect-eq !>
|
||||
:- ~
|
||||
queue.c
|
||||
::
|
||||
=/ results4
|
||||
%- expect-eq !>
|
||||
:- 0
|
||||
max-size.c
|
||||
:: trying to get an element just returns ~
|
||||
::
|
||||
=^ maybe1 c (~(get by-clock c) 3)
|
||||
=/ results5
|
||||
%- expect-eq !>
|
||||
:- ~
|
||||
maybe1
|
||||
:: trying to put an element in doesn't mutate the clock
|
||||
::
|
||||
=. c (~(put by-clock c) 4 "four")
|
||||
::
|
||||
=/ results6
|
||||
%- expect-eq !>
|
||||
:- 0
|
||||
size.c
|
||||
::
|
||||
;: weld
|
||||
results1
|
||||
results2
|
||||
results3
|
||||
results4
|
||||
results5
|
||||
results6
|
||||
==
|
||||
--
|
Loading…
Reference in New Issue
Block a user