From 1101ed699a0c3c23c0bb11267d390febfdc02409 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 2 Sep 2024 16:41:09 -0400 Subject: Introduce result_view.lua, and use it in LLInventory.lua. result_view(key_length, fetch) returns a virtual view of a potentially-large C++ result set. Given the result-set key, its total length and a function fetch(key, start) => (slice, adjusted start), the read-only table returned by result_view() manages indexed access and table iteration over the entire result set, fetching a slice at a time as required. Change LLInventory to use result_view() instead of only ever fetching the first slice of a result set. TODO: This depends on the viewer's "LLInventory" listener returning the total result set length as well as the result set key. It does not yet return the length. --- indra/newview/scripts/lua/require/LLInventory.lua | 38 +++++++------- indra/newview/scripts/lua/require/result_view.lua | 62 +++++++++++++++++++++++ 2 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 indra/newview/scripts/lua/require/result_view.lua (limited to 'indra/newview') diff --git a/indra/newview/scripts/lua/require/LLInventory.lua b/indra/newview/scripts/lua/require/LLInventory.lua index 0ff6b9fb37..ce501e75f3 100644 --- a/indra/newview/scripts/lua/require/LLInventory.lua +++ b/indra/newview/scripts/lua/require/LLInventory.lua @@ -1,12 +1,14 @@ local leap = require 'leap' local mapargs = require 'mapargs' +local result_view = require 'result_view' local function result(keys) return LL.setdtor( 'LLInventory result', setmetatable( -- the basic table wrapped by setmetatable just captures the int - -- result-set keys from 'keys', but with underscore prefixes + -- result-set {key, length} pairs from 'keys', but with underscore + -- prefixes { _categories=keys.categories, _items=keys.items, @@ -15,7 +17,7 @@ local function result(keys) close = function(self) leap.send('LLInventory', {op='closeResult', - result={self._categories, self._items}}) + result={self._categories[1], self._items[1]}}) end }, -- The caller of one of our methods that returns a result set @@ -24,25 +26,25 @@ local function result(keys) -- either 'categories' or 'items', the __index() metamethod -- populates that field. { - __index = function(t, key) + __index = function(t, field) -- we really don't care about references to any other field - if not table.find({'categories', 'items'}, key) then + if not table.find({'categories', 'items'}, field) then return nil end - -- We cleverly saved the int result set key in a field - -- with the same name but an underscore prefix. - local resultkey = t['_' .. key] - -- TODO: This only ever fetches the FIRST slice. What we - -- really want is to return a table with metamethods that - -- manage indexed access and table iteration. - -- Remember our C++ entry point uses 0-relative indexing. - local slice = leap.request( - 'LLInventory', - {op='getSlice', result=resultkey, index=0}).slice - print(`getSlice({resultkey}, 0) => {slice} ({#slice} entries)`) - -- cache this slice for future reference - t[key] = slice - return slice + local view = result_view( + -- We cleverly saved the result set {key, length} pair in + -- a field with the same name but an underscore prefix. + t['_' .. field], + function(key, start) + local fetched = leap.request( + 'LLInventory', + {op='getSlice', result=key, index=start}) + return fetched.slice, fetched.start + end + ) + -- cache that view for future reference + t[field] = view + return view end } ), diff --git a/indra/newview/scripts/lua/require/result_view.lua b/indra/newview/scripts/lua/require/result_view.lua new file mode 100644 index 0000000000..4a58636f2f --- /dev/null +++ b/indra/newview/scripts/lua/require/result_view.lua @@ -0,0 +1,62 @@ +-- result_view(key_length, fetch) returns a table which stores only a slice +-- of a result set plus some control values, yet presents read-only virtual +-- access to the entire result set. +-- key_length: {result set key, total result set length} +-- fetch: function(key, start) that returns (slice, adjusted start) +local function result_view(key_length, fetch) + return setmetatable( + { + key=key_length[1], + length=key_length[2], + -- C++ result sets use 0-based indexing, so internally we do too + start=0, + -- start with a dummy array with length 0 + slice={} + }, + { + __len = function(this) + return this.length + end, + __index = function(this, i) + -- right away, convert to 0-relative indexing + i -= 1 + -- can we find this index within the current slice? + local reli = i - this.start + if 0 <= reli and reli < #this.slice then + return this.slice[reli] + end + -- is this index outside the overall result set? + if not (0 <= i and i < this.length) then + return nil + end + -- fetch a new slice starting at i, using provided fetch() + local start + this.slice, start = fetch(key, i) + -- It's possible that caller-provided fetch() function forgot + -- to return the adjusted start index of the new slice. In + -- Lua, 0 tests as true, so if fetch() returned (slice, 0), + -- we'll duly reset this.start to 0. + if start then + this.start = start + end + -- hopefully this slice contains the desired i + return this.slice[i - this.start] + end, + -- We purposely avoid putting any array entries (int keys) into + -- our table so that access to any int key will always call our + -- __index() metamethod. Moreover, we want any table iteration to + -- call __index(table, i) however many times; we do NOT want it to + -- retrieve key, length, start, slice. + -- So turn 'for k, v in result' into 'for k, v in ipairs(result)'. + __iter = ipairs, + -- This result set provides read-only access. + -- We do not support pushing updates to individual items back to + -- C++; for the intended use cases, that makes no sense. + __newindex = function(this, i, value) + error("result_view is a read-only data structure", 2) + end + } + ) +end + +return result_view -- cgit v1.2.3