Skip to content

Commit 0f5732b

Browse files
authored
Add withValue for immutable tables (#24825)
This change adds `withValue` templates for the `Table` type that are able to operate on immutable table values -- the existing implementation requires a `var`. This is needed for situations where performance is sensitive. There are two goals with my implementation: 1. Don't create a copy of the value in the table. That's why I need the `cursor` pragma. Otherwise, it would copy the value 2. Don't double calculate the hash. That's kind of intrinsic with this implementation. But the only way to achieve this without this PR is to first check `if key in table` then to read `table[key]` I brought this up in the discord and a few folks tried to come up with options that were as fast as this, but nothing quite matched the performance here. Thread starts here: https://discord.com/channels/371759389889003530/371759389889003532/1355206546966974584
1 parent e0a4876 commit 0f5732b

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

lib/pure/collections/tables.nim

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,68 @@ template withValue*[A, B](t: var Table[A, B], key: A,
676676
else:
677677
body2
678678

679+
template withValue*[A, B](t: Table[A, B], key: A,
680+
value, body1, body2: untyped) =
681+
## Retrieves the value at `t[key]` if it exists, assigns
682+
## it to the variable `value` and executes `body`
683+
runnableExamples:
684+
type
685+
User = object
686+
name: string
687+
688+
proc `=copy`(dest: var User, source: User) {.error.}
689+
690+
proc exec(t: Table[int, User]) =
691+
t.withValue(1, value):
692+
assert value.name == "Hello"
693+
do:
694+
doAssert false
695+
696+
var executedElseBranch = false
697+
t.withValue(521, value):
698+
doAssert false
699+
do:
700+
executedElseBranch = true
701+
assert executedElseBranch
702+
703+
var t = initTable[int, User]()
704+
t[1] = User(name: "Hello")
705+
t.exec()
706+
707+
mixin rawGet
708+
var hc: Hash
709+
var index = rawGet(t, key, hc)
710+
if index > 0:
711+
let value {.cursor, inject.} = t.data[index].val
712+
body1
713+
else:
714+
body2
715+
716+
template withValue*[A, B](t: Table[A, B], key: A,
717+
value, body: untyped) =
718+
## Retrieves the value at `t[key]` if it exists, assigns
719+
## it to the variable `value` and executes `body`
720+
runnableExamples:
721+
type
722+
User = object
723+
name: string
724+
725+
proc `=copy`(dest: var User, source: User) {.error.}
726+
727+
proc exec(t: Table[int, User]) =
728+
t.withValue(1, value):
729+
assert value.name == "Hello"
730+
731+
t.withValue(521, value):
732+
doAssert false
733+
734+
var t = initTable[int, User]()
735+
t[1] = User(name: "Hello")
736+
t.exec()
737+
738+
withValue(t, key, value, body):
739+
discard
740+
679741

680742
iterator pairs*[A, B](t: Table[A, B]): (A, B) =
681743
## Iterates over any `(key, value)` pair in the table `t`.

0 commit comments

Comments
 (0)