Merge branch 'master' into src-included
Conflicts: rebar
This commit is contained in:
commit
8b1bfbd334
21 changed files with 4908 additions and 653 deletions
257
README.md
257
README.md
|
@ -9,7 +9,8 @@ __Authors:__ Joseph Wayne Norton ([`norton@alum.mit.edu`](mailto:norton@alum.mit
|
||||||
storage implementation. LETS tries to address some bad properties of
|
storage implementation. LETS tries to address some bad properties of
|
||||||
ETS and DETS. ETS is limited by physical memory. DETS is limited by
|
ETS and DETS. ETS is limited by physical memory. DETS is limited by
|
||||||
a 2 GB file size limitation and does not implement ordered sets. LETS
|
a 2 GB file size limitation and does not implement ordered sets. LETS
|
||||||
has neither of these limitations.</p>
|
has neither of these limitations. Data can also be automatically
|
||||||
|
compressed using the Snappy compression library.</p>
|
||||||
<p>For testing and comparison purposes, LETS supports three
|
<p>For testing and comparison purposes, LETS supports three
|
||||||
implementations:</p>
|
implementations:</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -30,35 +31,10 @@ implementations:</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>LETS is not intended to be an exact clone of ETS. The currently
|
<p>LETS is not intended to be an exact clone of ETS. The currently
|
||||||
supported APIs are:</p>
|
supported ETS APIs are:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<tt>new/2</tt>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>destroy/2</tt> <em>only driver and nif implementations</em>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>repair/2</tt> <em>only driver and nif implementations</em>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>insert/2</tt>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>insert_new/2</tt> <em>only the ets implementation</em>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>delete/1</tt>
|
<tt>delete/1</tt>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -69,12 +45,7 @@ supported APIs are:</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<tt>delete_all_objects/1</tt> <em>only the ets implementation</em>
|
<tt>delete_all_objects/1</tt> <em>only ets implementation</em>
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>lookup/2</tt>
|
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -84,7 +55,17 @@ supported APIs are:</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<tt>next/2</tt>
|
<tt>foldl/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>foldr/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>info/1</tt> <em>only a subset of items</em>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -94,6 +75,126 @@ supported APIs are:</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
|
<tt>insert/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>insert_new/2</tt> <em>only ets implementation</em>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>last/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>lookup/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>lookup_element/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match_delete/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match_object/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match_object/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match_object/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>member/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>new/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>next/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>prev/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_count/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_delete/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_reverse/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_reverse/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_reverse/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
<tt>tab2list/1</tt>
|
<tt>tab2list/1</tt>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -127,7 +228,7 @@ please read further.</p>
|
||||||
|
|
||||||
<h3 id="_where_should_i_start">Where should I start?</h3>
|
<h3 id="_where_should_i_start">Where should I start?</h3>
|
||||||
<p>This README is the only bit of documentation right now.</p>
|
<p>This README is the only bit of documentation right now.</p>
|
||||||
<p>The QC (a.k.a. QuickCheck, Proper, etc.) tests underneath the
|
<p>The QC (a.k.a. QuickCheck, PropEr, etc.) tests underneath the
|
||||||
"tests/qc" directory should be helpful for understanding the
|
"tests/qc" directory should be helpful for understanding the
|
||||||
specification and behavior of ETS and LETS. These QC tests also
|
specification and behavior of ETS and LETS. These QC tests also
|
||||||
illustrate several strategies for testing Erlang Driver-based and
|
illustrate several strategies for testing Erlang Driver-based and
|
||||||
|
@ -148,6 +249,11 @@ provides an ordered mapping from string keys to string values.</p>
|
||||||
<p>See <a href="http://code.google.com/p/leveldb/">http://code.google.com/p/leveldb/</a> for further details.</p>
|
<p>See <a href="http://code.google.com/p/leveldb/">http://code.google.com/p/leveldb/</a> for further details.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 id="_what_is_snappy">What is Snappy?</h3>
|
||||||
|
<p>Snappy is a fast compression/decompression library written at Google.</p>
|
||||||
|
<p>See <a href="http://code.google.com/p/snappy/">http://code.google.com/p/snappy/</a> for further details.</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2 id="_to_download">To download</h2>
|
<h2 id="_to_download">To download</h2>
|
||||||
|
@ -415,7 +521,36 @@ Run 5,000 QuickCheck tests
|
||||||
<pre><tt>$ cd working-directory-name/src/lib/lets/.eunit
|
<pre><tt>$ cd working-directory-name/src/lib/lets/.eunit
|
||||||
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
||||||
|
|
||||||
1> qc_statem_lets:run(5000).
|
1> qc_statem_lets:qc_run(5000).
|
||||||
|
....
|
||||||
|
OK, passed 5000 tests
|
||||||
|
|
||||||
|
9.022% {delete,ok}
|
||||||
|
7.800% {new,ok}
|
||||||
|
4.535% {match_delete,ok}
|
||||||
|
4.491% {lookup,ok}
|
||||||
|
4.399% {select,ok}
|
||||||
|
4.352% {select_delete,ok}
|
||||||
|
4.348% {tab2list,ok}
|
||||||
|
4.341% {member,ok}
|
||||||
|
4.334% {last,ok}
|
||||||
|
4.315% {foldl,ok}
|
||||||
|
4.308% {select_reverse,ok}
|
||||||
|
4.301% {select_count,ok}
|
||||||
|
4.293% {select31,ok}
|
||||||
|
4.264% {first,ok}
|
||||||
|
4.216% {foldr,ok}
|
||||||
|
4.202% {match_object,ok}
|
||||||
|
4.184% {match,ok}
|
||||||
|
4.056% {insert,ok}
|
||||||
|
3.997% {prev,ok}
|
||||||
|
3.774% {next,ok}
|
||||||
|
3.416% {lookup_element,{error,badarg}}
|
||||||
|
1.298% {insert_new,ok}
|
||||||
|
0.757% {lookup_element,ok}
|
||||||
|
0.516% {next,{error,badarg}}
|
||||||
|
0.483% {prev,{error,badarg}}
|
||||||
|
true
|
||||||
.......</tt></pre>
|
.......</tt></pre>
|
||||||
|
|
||||||
|
|
||||||
|
@ -424,7 +559,7 @@ $ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
||||||
Tip
|
Tip
|
||||||
</td>
|
</td>
|
||||||
<td class="content">For testing LevelDB directly using the C bindings, try
|
<td class="content">For testing LevelDB directly using the C bindings, try
|
||||||
<tt>qc_statemc_lets:run(5000)</tt>.</td>
|
<tt>qc_statemc_lets:qc_run(5000)</tt>.</td>
|
||||||
</tr></table>
|
</tr></table>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
@ -432,12 +567,12 @@ Tip
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2 id="_to_test_proper">To test - Proper</h2>
|
<h2 id="_to_test_proper">To test - PropEr</h2>
|
||||||
|
|
||||||
<ol class="arabic">
|
<ol class="arabic">
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
Make sure Proper is in your Erlang code path. One simple way to
|
Make sure PropEr is in your Erlang code path. One simple way to
|
||||||
accomplish this is by adding the code path to your <tt>~/.erlang</tt>
|
accomplish this is by adding the code path to your <tt>~/.erlang</tt>
|
||||||
resource file.
|
resource file.
|
||||||
</p>
|
</p>
|
||||||
|
@ -448,7 +583,7 @@ Make sure Proper is in your Erlang code path. One simple way to
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
Compile for Proper
|
Compile for PropEr
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
@ -459,14 +594,43 @@ $ make compile-proper proper-compile</tt></pre>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
Run 5,000 Proper tests
|
Run 5,000 PropEr tests
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<pre><tt>$ cd working-directory-name/src/lib/lets/.eunit
|
<pre><tt>$ cd working-directory-name/src/lib/lets/.eunit
|
||||||
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
||||||
|
|
||||||
1> qc_statem_lets:run(5000).
|
1> qc_statem_lets:qc_run(5000).
|
||||||
|
....
|
||||||
|
OK: Passed 5000 test(s).
|
||||||
|
|
||||||
|
11% {new,ok}
|
||||||
|
8% {delete,ok}
|
||||||
|
4% {member,ok}
|
||||||
|
4% {select,ok}
|
||||||
|
4% {select_count,ok}
|
||||||
|
4% {select_reverse,ok}
|
||||||
|
4% {lookup,ok}
|
||||||
|
4% {match_object,ok}
|
||||||
|
4% {tab2list,ok}
|
||||||
|
4% {last,ok}
|
||||||
|
4% {match,ok}
|
||||||
|
4% {foldl,ok}
|
||||||
|
4% {match_delete,ok}
|
||||||
|
3% {prev,ok}
|
||||||
|
3% {select31,ok}
|
||||||
|
3% {select_delete,ok}
|
||||||
|
3% {foldr,ok}
|
||||||
|
3% {insert,ok}
|
||||||
|
3% {first,ok}
|
||||||
|
3% {next,ok}
|
||||||
|
3% {lookup_element,{error,badarg}}
|
||||||
|
1% {insert_new,ok}
|
||||||
|
0% {prev,{error,badarg}}
|
||||||
|
0% {lookup_element,ok}
|
||||||
|
0% {next,{error,badarg}}
|
||||||
|
true
|
||||||
.......</tt></pre>
|
.......</tt></pre>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
@ -484,7 +648,7 @@ Documentation
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
Explain how to run QuickCheck/Proper tests using a new rebar
|
Explain how to run QuickCheck/PropEr tests using a new rebar
|
||||||
plugin.
|
plugin.
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -549,6 +713,13 @@ New APIs (TBD)
|
||||||
(<a href="http://code.google.com/p/leveldb/issues/detail?id=43">http://code.google.com/p/leveldb/issues/detail?id=43</a>)
|
(<a href="http://code.google.com/p/leveldb/issues/detail?id=43">http://code.google.com/p/leveldb/issues/detail?id=43</a>)
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
Add custom (i.e. not supported by native ETS) APIs for providing
|
||||||
|
access to LevelDB's iterators for <tt>drv</tt> and <tt>nif</tt> backend
|
||||||
|
implementations.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -35,24 +35,32 @@
|
||||||
|
|
||||||
#define LETS_BADARG 0x00
|
#define LETS_BADARG 0x00
|
||||||
#define LETS_TRUE 0x01
|
#define LETS_TRUE 0x01
|
||||||
#define LETS_END_OF_TABLE 0x02
|
#define LETS_FALSE 0x02
|
||||||
#define LETS_BINARY 0x03
|
#define LETS_END_OF_TABLE 0x03
|
||||||
|
#define LETS_BINARY 0x04
|
||||||
|
|
||||||
#define LETS_OPEN6 0x00 // same as OPEN
|
#define LETS_OPEN6 0x00 // same as OPEN
|
||||||
#define LETS_DESTROY6 0x01 // same as DESTROY
|
#define LETS_DESTROY6 0x01 // same as DESTROY
|
||||||
#define LETS_REPAIR6 0x02 // same as REPAIR
|
#define LETS_REPAIR6 0x02 // same as REPAIR
|
||||||
#define LETS_INSERT2 0x03
|
#define LETS_DELETE1 0x03
|
||||||
#define LETS_INSERT3 0x04
|
#define LETS_DELETE2 0x04
|
||||||
#define LETS_INSERT_NEW2 0x05
|
#define LETS_DELETE_ALL_OBJECTS1 0x05
|
||||||
#define LETS_INSERT_NEW3 0x06
|
#define LETS_FIRST1 0x06
|
||||||
#define LETS_DELETE1 0x07
|
#define LETS_FIRST_ITER1 0x07
|
||||||
#define LETS_DELETE2 0x08
|
#define LETS_INFO_MEMORY1 0x08
|
||||||
#define LETS_DELETE_ALL_OBJECTS1 0x09
|
#define LETS_INFO_SIZE1 0x09
|
||||||
#define LETS_LOOKUP2 0x0A
|
#define LETS_INSERT2 0x0A
|
||||||
#define LETS_FIRST1 0x0B
|
#define LETS_INSERT3 0x0B
|
||||||
#define LETS_NEXT2 0x0C
|
#define LETS_INSERT_NEW2 0x0C
|
||||||
#define LETS_INFO_MEMORY1 0x0D
|
#define LETS_INSERT_NEW3 0x0D
|
||||||
#define LETS_INFO_SIZE1 0x0E
|
#define LETS_LAST1 0x0E
|
||||||
|
#define LETS_LAST_ITER1 0x0F
|
||||||
|
#define LETS_LOOKUP2 0x10
|
||||||
|
#define LETS_MEMBER2 0x11
|
||||||
|
#define LETS_NEXT2 0x12
|
||||||
|
#define LETS_NEXT_ITER2 0x13
|
||||||
|
#define LETS_PREV2 0x14
|
||||||
|
#define LETS_PREV_ITER2 0x15
|
||||||
|
|
||||||
// DrvData
|
// DrvData
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -121,10 +129,24 @@ static void lets_async_delete2(void* async_data);
|
||||||
// static void lets_async_delete_all_objects1(void* async_data);
|
// static void lets_async_delete_all_objects1(void* async_data);
|
||||||
static void lets_output_lookup2(DrvData* d, char* buf, int len, int* index, int items);
|
static void lets_output_lookup2(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
static void lets_async_lookup2(void* async_data);
|
static void lets_async_lookup2(void* async_data);
|
||||||
|
static void lets_output_member2(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
|
static void lets_async_member2(void* async_data);
|
||||||
static void lets_output_first1(DrvData* d, char* buf, int len, int* index, int items);
|
static void lets_output_first1(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
static void lets_async_first1(void* async_data);
|
static void lets_async_first1(void* async_data);
|
||||||
|
static void lets_output_first_iter1(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
|
static void lets_async_first_iter1(void* async_data);
|
||||||
|
static void lets_output_last1(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
|
static void lets_async_last1(void* async_data);
|
||||||
|
static void lets_output_last_iter1(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
|
static void lets_async_last_iter1(void* async_data);
|
||||||
static void lets_output_next2(DrvData* d, char* buf, int len, int* index, int items);
|
static void lets_output_next2(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
static void lets_async_next2(void* async_data);
|
static void lets_async_next2(void* async_data);
|
||||||
|
static void lets_output_next_iter2(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
|
static void lets_async_next_iter2(void* async_data);
|
||||||
|
static void lets_output_prev2(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
|
static void lets_async_prev2(void* async_data);
|
||||||
|
static void lets_output_prev_iter2(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
|
static void lets_async_prev_iter2(void* async_data);
|
||||||
// static void lets_output_info_memory1(DrvData* d, char* buf, int len, int* index, int items);
|
// static void lets_output_info_memory1(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
// static void lets_async_info_memory1(void* async_data);
|
// static void lets_async_info_memory1(void* async_data);
|
||||||
// static void lets_output_info_size1(DrvData* d, char* buf, int len, int* index, int items);
|
// static void lets_output_info_size1(DrvData* d, char* buf, int len, int* index, int items);
|
||||||
|
@ -358,16 +380,51 @@ drv_output(ErlDrvData handle, char* buf, int len)
|
||||||
if (ng) GOTOBADARG;
|
if (ng) GOTOBADARG;
|
||||||
lets_output_lookup2(d, buf, len, &index, items);
|
lets_output_lookup2(d, buf, len, &index, items);
|
||||||
break;
|
break;
|
||||||
|
case LETS_MEMBER2:
|
||||||
|
ng = (items != 1 || !d->impl.alive);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
lets_output_member2(d, buf, len, &index, items);
|
||||||
|
break;
|
||||||
case LETS_FIRST1:
|
case LETS_FIRST1:
|
||||||
ng = (items != 0 || !d->impl.alive);
|
ng = (items != 0 || !d->impl.alive);
|
||||||
if (ng) GOTOBADARG;
|
if (ng) GOTOBADARG;
|
||||||
lets_output_first1(d, buf, len, &index, items);
|
lets_output_first1(d, buf, len, &index, items);
|
||||||
break;
|
break;
|
||||||
|
case LETS_FIRST_ITER1:
|
||||||
|
ng = (items != 0 || !d->impl.alive);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
lets_output_first_iter1(d, buf, len, &index, items);
|
||||||
|
break;
|
||||||
|
case LETS_LAST1:
|
||||||
|
ng = (items != 0 || !d->impl.alive);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
lets_output_last1(d, buf, len, &index, items);
|
||||||
|
break;
|
||||||
|
case LETS_LAST_ITER1:
|
||||||
|
ng = (items != 0 || !d->impl.alive);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
lets_output_last_iter1(d, buf, len, &index, items);
|
||||||
|
break;
|
||||||
case LETS_NEXT2:
|
case LETS_NEXT2:
|
||||||
ng = (items != 1 || !d->impl.alive);
|
ng = (items != 1 || !d->impl.alive);
|
||||||
if (ng) GOTOBADARG;
|
if (ng) GOTOBADARG;
|
||||||
lets_output_next2(d, buf, len, &index, items);
|
lets_output_next2(d, buf, len, &index, items);
|
||||||
break;
|
break;
|
||||||
|
case LETS_NEXT_ITER2:
|
||||||
|
ng = (items != 1 || !d->impl.alive);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
lets_output_next_iter2(d, buf, len, &index, items);
|
||||||
|
break;
|
||||||
|
case LETS_PREV2:
|
||||||
|
ng = (items != 1 || !d->impl.alive);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
lets_output_prev2(d, buf, len, &index, items);
|
||||||
|
break;
|
||||||
|
case LETS_PREV_ITER2:
|
||||||
|
ng = (items != 1 || !d->impl.alive);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
lets_output_prev_iter2(d, buf, len, &index, items);
|
||||||
|
break;
|
||||||
case LETS_INFO_MEMORY1:
|
case LETS_INFO_MEMORY1:
|
||||||
ng = (items != 0 || !d->impl.alive);
|
ng = (items != 0 || !d->impl.alive);
|
||||||
if (ng) GOTOBADARG;
|
if (ng) GOTOBADARG;
|
||||||
|
@ -402,6 +459,9 @@ drv_ready_async(ErlDrvData handle, ErlDrvThreadData async_data)
|
||||||
case LETS_TRUE:
|
case LETS_TRUE:
|
||||||
driver_send_int(d, LETS_TRUE, a->caller);
|
driver_send_int(d, LETS_TRUE, a->caller);
|
||||||
break;
|
break;
|
||||||
|
case LETS_FALSE:
|
||||||
|
driver_send_int(d, LETS_FALSE, a->caller);
|
||||||
|
break;
|
||||||
case LETS_END_OF_TABLE:
|
case LETS_END_OF_TABLE:
|
||||||
driver_send_int(d, LETS_END_OF_TABLE, a->caller);
|
driver_send_int(d, LETS_END_OF_TABLE, a->caller);
|
||||||
break;
|
break;
|
||||||
|
@ -819,6 +879,7 @@ lets_async_delete2(void* async_data)
|
||||||
a->reply = LETS_TRUE;
|
a->reply = LETS_TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
lets_output_lookup2(DrvData* d, char* buf, int len, int* index, int items)
|
lets_output_lookup2(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
{
|
{
|
||||||
|
@ -894,6 +955,73 @@ lets_async_lookup2(void* async_data)
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_output_member2(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
|
{
|
||||||
|
DrvAsync* drv_async = NULL;
|
||||||
|
int ng;
|
||||||
|
char *key;
|
||||||
|
long keylen;
|
||||||
|
|
||||||
|
ng = ei_inspect_binary(buf, index, (void**) &key, &keylen);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
|
||||||
|
if (d->impl.async) {
|
||||||
|
drv_async = new DrvAsync(d, driver_caller(d->port), LETS_MEMBER2, (const char*) key, keylen);
|
||||||
|
if (!drv_async) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
driver_async(d->port, NULL, lets_async_member2, drv_async, drv_async_free);
|
||||||
|
} else {
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) key, keylen);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid() || it->key().compare(skey) != 0) {
|
||||||
|
driver_send_int(d, LETS_FALSE);
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver_send_int(d, LETS_TRUE);
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
badarg:
|
||||||
|
if (drv_async) { delete drv_async; }
|
||||||
|
driver_send_int(d, LETS_BADARG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_async_member2(void* async_data)
|
||||||
|
{
|
||||||
|
DrvAsync* a = (DrvAsync*) async_data;
|
||||||
|
assert(a != NULL);
|
||||||
|
DrvData* d = a->drvdata;
|
||||||
|
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) a->binary->orig_bytes, a->binary->orig_size);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid() || it->key().compare(skey) != 0) {
|
||||||
|
a->reply = LETS_FALSE;
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
a->reply = LETS_TRUE;
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
lets_output_first1(DrvData* d, char* buf, int len, int* index, int items)
|
lets_output_first1(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
{
|
{
|
||||||
|
@ -961,6 +1089,208 @@ lets_async_first1(void* async_data)
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_output_first_iter1(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
|
{
|
||||||
|
DrvAsync* drv_async = NULL;
|
||||||
|
|
||||||
|
if (d->impl.async) {
|
||||||
|
drv_async = new DrvAsync(d, driver_caller(d->port), LETS_FIRST_ITER1);
|
||||||
|
if (!drv_async) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
driver_async(d->port, NULL, lets_async_first_iter1, drv_async, drv_async_free);
|
||||||
|
} else {
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->SeekToFirst();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
driver_send_int(d, LETS_END_OF_TABLE);
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver_send_buf(d, it->value().data(), it->value().size());
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
badarg:
|
||||||
|
if (drv_async) { delete drv_async; }
|
||||||
|
driver_send_int(d, LETS_BADARG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_async_first_iter1(void* async_data)
|
||||||
|
{
|
||||||
|
DrvAsync* a = (DrvAsync*) async_data;
|
||||||
|
assert(a != NULL);
|
||||||
|
DrvData* d = a->drvdata;
|
||||||
|
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->SeekToFirst();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
a->reply = LETS_END_OF_TABLE;
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErlDrvBinary* binary = driver_alloc_binary(it->value().size());
|
||||||
|
if (binary) {
|
||||||
|
memcpy(binary->orig_bytes, it->value().data(), binary->orig_size);
|
||||||
|
a->binary = binary;
|
||||||
|
a->reply = LETS_BINARY;
|
||||||
|
} else {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_output_last1(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
|
{
|
||||||
|
DrvAsync* drv_async = NULL;
|
||||||
|
|
||||||
|
if (d->impl.async) {
|
||||||
|
drv_async = new DrvAsync(d, driver_caller(d->port), LETS_LAST1);
|
||||||
|
if (!drv_async) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
driver_async(d->port, NULL, lets_async_last1, drv_async, drv_async_free);
|
||||||
|
} else {
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->SeekToLast();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
driver_send_int(d, LETS_END_OF_TABLE);
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver_send_buf(d, it->key().data(), it->key().size());
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
badarg:
|
||||||
|
if (drv_async) { delete drv_async; }
|
||||||
|
driver_send_int(d, LETS_BADARG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_async_last1(void* async_data)
|
||||||
|
{
|
||||||
|
DrvAsync* a = (DrvAsync*) async_data;
|
||||||
|
assert(a != NULL);
|
||||||
|
DrvData* d = a->drvdata;
|
||||||
|
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->SeekToLast();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
a->reply = LETS_END_OF_TABLE;
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErlDrvBinary* binary = driver_alloc_binary(it->key().size());
|
||||||
|
if (binary) {
|
||||||
|
memcpy(binary->orig_bytes, it->key().data(), binary->orig_size);
|
||||||
|
a->binary = binary;
|
||||||
|
a->reply = LETS_BINARY;
|
||||||
|
} else {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_output_last_iter1(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
|
{
|
||||||
|
DrvAsync* drv_async = NULL;
|
||||||
|
|
||||||
|
if (d->impl.async) {
|
||||||
|
drv_async = new DrvAsync(d, driver_caller(d->port), LETS_LAST_ITER1);
|
||||||
|
if (!drv_async) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
driver_async(d->port, NULL, lets_async_last_iter1, drv_async, drv_async_free);
|
||||||
|
} else {
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->SeekToLast();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
driver_send_int(d, LETS_END_OF_TABLE);
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver_send_buf(d, it->value().data(), it->value().size());
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
badarg:
|
||||||
|
if (drv_async) { delete drv_async; }
|
||||||
|
driver_send_int(d, LETS_BADARG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_async_last_iter1(void* async_data)
|
||||||
|
{
|
||||||
|
DrvAsync* a = (DrvAsync*) async_data;
|
||||||
|
assert(a != NULL);
|
||||||
|
DrvData* d = a->drvdata;
|
||||||
|
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->SeekToLast();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
a->reply = LETS_END_OF_TABLE;
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErlDrvBinary* binary = driver_alloc_binary(it->value().size());
|
||||||
|
if (binary) {
|
||||||
|
memcpy(binary->orig_bytes, it->value().data(), binary->orig_size);
|
||||||
|
a->binary = binary;
|
||||||
|
a->reply = LETS_BINARY;
|
||||||
|
} else {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
lets_output_next2(DrvData* d, char* buf, int len, int* index, int items)
|
lets_output_next2(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
{
|
{
|
||||||
|
@ -1052,3 +1382,268 @@ lets_async_next2(void* async_data)
|
||||||
|
|
||||||
delete it;
|
delete it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_output_next_iter2(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
|
{
|
||||||
|
DrvAsync* drv_async = NULL;
|
||||||
|
int ng;
|
||||||
|
char *key;
|
||||||
|
long keylen;
|
||||||
|
|
||||||
|
ng = ei_inspect_binary(buf, index, (void**) &key, &keylen);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
|
||||||
|
if (d->impl.async) {
|
||||||
|
drv_async = new DrvAsync(d, driver_caller(d->port), LETS_NEXT_ITER2, (const char*) key, keylen);
|
||||||
|
if (!drv_async) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
driver_async(d->port, NULL, lets_async_next_iter2, drv_async, drv_async_free);
|
||||||
|
} else {
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) key, keylen);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid()) {
|
||||||
|
driver_send_int(d, LETS_END_OF_TABLE);
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->key().compare(skey) == 0) {
|
||||||
|
it->Next();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
driver_send_int(d, LETS_END_OF_TABLE);
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
driver_send_buf(d, it->value().data(), it->value().size());
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
badarg:
|
||||||
|
if (drv_async) { delete drv_async; }
|
||||||
|
driver_send_int(d, LETS_BADARG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_async_next_iter2(void* async_data)
|
||||||
|
{
|
||||||
|
DrvAsync* a = (DrvAsync*) async_data;
|
||||||
|
assert(a != NULL);
|
||||||
|
DrvData* d = a->drvdata;
|
||||||
|
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) a->binary->orig_bytes, a->binary->orig_size);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid()) {
|
||||||
|
a->reply = LETS_END_OF_TABLE;
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->key().compare(skey) == 0) {
|
||||||
|
it->Next();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
a->reply = LETS_END_OF_TABLE;
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErlDrvBinary* binary = driver_realloc_binary(a->binary, it->value().size());
|
||||||
|
if (binary) {
|
||||||
|
memcpy(binary->orig_bytes, it->value().data(), binary->orig_size);
|
||||||
|
a->binary = binary;
|
||||||
|
a->reply = LETS_BINARY;
|
||||||
|
} else {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_output_prev2(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
|
{
|
||||||
|
DrvAsync* drv_async = NULL;
|
||||||
|
int ng;
|
||||||
|
char *key;
|
||||||
|
long keylen;
|
||||||
|
|
||||||
|
ng = ei_inspect_binary(buf, index, (void**) &key, &keylen);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
|
||||||
|
if (d->impl.async) {
|
||||||
|
drv_async = new DrvAsync(d, driver_caller(d->port), LETS_PREV2, (const char*) key, keylen);
|
||||||
|
if (!drv_async) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
driver_async(d->port, NULL, lets_async_prev2, drv_async, drv_async_free);
|
||||||
|
} else {
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) key, keylen);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid()) {
|
||||||
|
it->SeekToLast();
|
||||||
|
} else {
|
||||||
|
it->Prev();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!it->Valid()) {
|
||||||
|
driver_send_int(d, LETS_END_OF_TABLE);
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver_send_buf(d, it->key().data(), it->key().size());
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
badarg:
|
||||||
|
if (drv_async) { delete drv_async; }
|
||||||
|
driver_send_int(d, LETS_BADARG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_async_prev2(void* async_data)
|
||||||
|
{
|
||||||
|
DrvAsync* a = (DrvAsync*) async_data;
|
||||||
|
assert(a != NULL);
|
||||||
|
DrvData* d = a->drvdata;
|
||||||
|
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) a->binary->orig_bytes, a->binary->orig_size);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid()) {
|
||||||
|
it->SeekToLast();
|
||||||
|
} else {
|
||||||
|
it->Prev();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!it->Valid()) {
|
||||||
|
a->reply = LETS_END_OF_TABLE;
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErlDrvBinary* binary = driver_realloc_binary(a->binary, it->key().size());
|
||||||
|
if (binary) {
|
||||||
|
memcpy(binary->orig_bytes, it->key().data(), binary->orig_size);
|
||||||
|
a->binary = binary;
|
||||||
|
a->reply = LETS_BINARY;
|
||||||
|
} else {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_output_prev_iter2(DrvData* d, char* buf, int len, int* index, int items)
|
||||||
|
{
|
||||||
|
DrvAsync* drv_async = NULL;
|
||||||
|
int ng;
|
||||||
|
char *key;
|
||||||
|
long keylen;
|
||||||
|
|
||||||
|
ng = ei_inspect_binary(buf, index, (void**) &key, &keylen);
|
||||||
|
if (ng) GOTOBADARG;
|
||||||
|
|
||||||
|
if (d->impl.async) {
|
||||||
|
drv_async = new DrvAsync(d, driver_caller(d->port), LETS_PREV_ITER2, (const char*) key, keylen);
|
||||||
|
if (!drv_async) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
driver_async(d->port, NULL, lets_async_prev_iter2, drv_async, drv_async_free);
|
||||||
|
} else {
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
GOTOBADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) key, keylen);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid()) {
|
||||||
|
it->SeekToLast();
|
||||||
|
} else {
|
||||||
|
it->Prev();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!it->Valid()) {
|
||||||
|
driver_send_int(d, LETS_END_OF_TABLE);
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver_send_buf(d, it->value().data(), it->value().size());
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
badarg:
|
||||||
|
if (drv_async) { delete drv_async; }
|
||||||
|
driver_send_int(d, LETS_BADARG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lets_async_prev_iter2(void* async_data)
|
||||||
|
{
|
||||||
|
DrvAsync* a = (DrvAsync*) async_data;
|
||||||
|
assert(a != NULL);
|
||||||
|
DrvData* d = a->drvdata;
|
||||||
|
|
||||||
|
leveldb::Iterator* it = d->impl.db->NewIterator(d->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) a->binary->orig_bytes, a->binary->orig_size);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid()) {
|
||||||
|
it->SeekToLast();
|
||||||
|
} else {
|
||||||
|
it->Prev();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!it->Valid()) {
|
||||||
|
a->reply = LETS_END_OF_TABLE;
|
||||||
|
delete it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErlDrvBinary* binary = driver_realloc_binary(a->binary, it->value().size());
|
||||||
|
if (binary) {
|
||||||
|
memcpy(binary->orig_bytes, it->value().data(), binary->orig_size);
|
||||||
|
a->binary = binary;
|
||||||
|
a->reply = LETS_BINARY;
|
||||||
|
} else {
|
||||||
|
a->reply = LETS_BADARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
|
@ -60,8 +60,15 @@ static ErlNifFunc nif_funcs[] =
|
||||||
{"impl_delete", 2, lets_nif_delete2},
|
{"impl_delete", 2, lets_nif_delete2},
|
||||||
{"impl_delete_all_objects", 1, lets_nif_delete_all_objects1},
|
{"impl_delete_all_objects", 1, lets_nif_delete_all_objects1},
|
||||||
{"impl_lookup", 2, lets_nif_lookup2},
|
{"impl_lookup", 2, lets_nif_lookup2},
|
||||||
|
{"impl_member", 2, lets_nif_member2},
|
||||||
{"impl_first", 1, lets_nif_first1},
|
{"impl_first", 1, lets_nif_first1},
|
||||||
|
{"impl_first_iter", 1, lets_nif_first_iter1},
|
||||||
|
{"impl_last", 1, lets_nif_last1},
|
||||||
|
{"impl_last_iter", 1, lets_nif_last_iter1},
|
||||||
{"impl_next", 2, lets_nif_next2},
|
{"impl_next", 2, lets_nif_next2},
|
||||||
|
{"impl_next_iter", 2, lets_nif_next_iter2},
|
||||||
|
{"impl_prev", 2, lets_nif_prev2},
|
||||||
|
{"impl_prev_iter", 2, lets_nif_prev_iter2},
|
||||||
{"impl_info_memory", 1, lets_nif_info_memory1},
|
{"impl_info_memory", 1, lets_nif_info_memory1},
|
||||||
{"impl_info_size", 1, lets_nif_info_size1},
|
{"impl_info_size", 1, lets_nif_info_size1},
|
||||||
};
|
};
|
||||||
|
@ -448,11 +455,45 @@ lets_nif_lookup2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
return blob;
|
return blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_member2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
{
|
||||||
|
lets_nif_handle* h;
|
||||||
|
ErlNifBinary key;
|
||||||
|
leveldb::Status status;
|
||||||
|
|
||||||
|
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
if (!enif_inspect_binary(env, argv[1], &key)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h->impl.alive) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) key.data, key.size);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid() || it->key().compare(skey) != 0) {
|
||||||
|
delete it;
|
||||||
|
return lets_atom_false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete it;
|
||||||
|
return lets_atom_true;
|
||||||
|
}
|
||||||
|
|
||||||
ERL_NIF_TERM
|
ERL_NIF_TERM
|
||||||
lets_nif_first1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
lets_nif_first1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
{
|
{
|
||||||
lets_nif_handle* h;
|
lets_nif_handle* h;
|
||||||
ERL_NIF_TERM first_key = 0;
|
ERL_NIF_TERM first = 0;
|
||||||
leveldb::Status status;
|
leveldb::Status status;
|
||||||
|
|
||||||
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
||||||
|
@ -475,7 +516,7 @@ lets_nif_first1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size = it->key().size();
|
size_t size = it->key().size();
|
||||||
unsigned char* k = enif_make_new_binary(env, size, &first_key);
|
unsigned char* k = enif_make_new_binary(env, size, &first);
|
||||||
if (!k) {
|
if (!k) {
|
||||||
delete it;
|
delete it;
|
||||||
return MAKEBADARG(env, status);
|
return MAKEBADARG(env, status);
|
||||||
|
@ -483,7 +524,121 @@ lets_nif_first1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
|
||||||
memcpy(k, it->key().data(), size);
|
memcpy(k, it->key().data(), size);
|
||||||
delete it;
|
delete it;
|
||||||
return first_key;
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_first_iter1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
{
|
||||||
|
lets_nif_handle* h;
|
||||||
|
ERL_NIF_TERM first = 0;
|
||||||
|
leveldb::Status status;
|
||||||
|
|
||||||
|
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h->impl.alive) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
it->SeekToFirst();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
delete it;
|
||||||
|
return lets_atom_end_of_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = it->value().size();
|
||||||
|
unsigned char* k = enif_make_new_binary(env, size, &first);
|
||||||
|
if (!k) {
|
||||||
|
delete it;
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(k, it->value().data(), size);
|
||||||
|
delete it;
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_last1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
{
|
||||||
|
lets_nif_handle* h;
|
||||||
|
ERL_NIF_TERM last = 0;
|
||||||
|
leveldb::Status status;
|
||||||
|
|
||||||
|
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h->impl.alive) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
it->SeekToLast();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
delete it;
|
||||||
|
return lets_atom_end_of_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = it->key().size();
|
||||||
|
unsigned char* k = enif_make_new_binary(env, size, &last);
|
||||||
|
if (!k) {
|
||||||
|
delete it;
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(k, it->key().data(), size);
|
||||||
|
delete it;
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_last_iter1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
{
|
||||||
|
lets_nif_handle* h;
|
||||||
|
ERL_NIF_TERM last = 0;
|
||||||
|
leveldb::Status status;
|
||||||
|
|
||||||
|
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h->impl.alive) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
it->SeekToLast();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
delete it;
|
||||||
|
return lets_atom_end_of_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = it->value().size();
|
||||||
|
unsigned char* k = enif_make_new_binary(env, size, &last);
|
||||||
|
if (!k) {
|
||||||
|
delete it;
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(k, it->value().data(), size);
|
||||||
|
delete it;
|
||||||
|
return last;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERL_NIF_TERM
|
ERL_NIF_TERM
|
||||||
|
@ -491,7 +646,7 @@ lets_nif_next2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
{
|
{
|
||||||
lets_nif_handle* h;
|
lets_nif_handle* h;
|
||||||
ErlNifBinary key;
|
ErlNifBinary key;
|
||||||
ERL_NIF_TERM next_key = 0;
|
ERL_NIF_TERM next = 0;
|
||||||
leveldb::Status status;
|
leveldb::Status status;
|
||||||
|
|
||||||
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
||||||
|
@ -526,7 +681,7 @@ lets_nif_next2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size = it->key().size();
|
size_t size = it->key().size();
|
||||||
unsigned char* k = enif_make_new_binary(env, size, &next_key);
|
unsigned char* k = enif_make_new_binary(env, size, &next);
|
||||||
if (!k) {
|
if (!k) {
|
||||||
delete it;
|
delete it;
|
||||||
return MAKEBADARG(env, status);
|
return MAKEBADARG(env, status);
|
||||||
|
@ -534,7 +689,156 @@ lets_nif_next2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
|
||||||
memcpy(k, it->key().data(), size);
|
memcpy(k, it->key().data(), size);
|
||||||
delete it;
|
delete it;
|
||||||
return next_key;
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_next_iter2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
{
|
||||||
|
lets_nif_handle* h;
|
||||||
|
ErlNifBinary key;
|
||||||
|
ERL_NIF_TERM next = 0;
|
||||||
|
leveldb::Status status;
|
||||||
|
|
||||||
|
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
if (!enif_inspect_binary(env, argv[1], &key)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h->impl.alive) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) key.data, key.size);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid()) {
|
||||||
|
delete it;
|
||||||
|
return lets_atom_end_of_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->key().compare(skey) == 0) {
|
||||||
|
it->Next();
|
||||||
|
if (!it->Valid()) {
|
||||||
|
delete it;
|
||||||
|
return lets_atom_end_of_table;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = it->value().size();
|
||||||
|
unsigned char* k = enif_make_new_binary(env, size, &next);
|
||||||
|
if (!k) {
|
||||||
|
delete it;
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(k, it->value().data(), size);
|
||||||
|
delete it;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_prev2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
{
|
||||||
|
lets_nif_handle* h;
|
||||||
|
ErlNifBinary key;
|
||||||
|
ERL_NIF_TERM prev = 0;
|
||||||
|
leveldb::Status status;
|
||||||
|
|
||||||
|
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
if (!enif_inspect_binary(env, argv[1], &key)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h->impl.alive) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) key.data, key.size);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid()) {
|
||||||
|
it->SeekToLast();
|
||||||
|
} else {
|
||||||
|
it->Prev();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!it->Valid()) {
|
||||||
|
delete it;
|
||||||
|
return lets_atom_end_of_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = it->key().size();
|
||||||
|
unsigned char* k = enif_make_new_binary(env, size, &prev);
|
||||||
|
if (!k) {
|
||||||
|
delete it;
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(k, it->key().data(), size);
|
||||||
|
delete it;
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_prev_iter2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
||||||
|
{
|
||||||
|
lets_nif_handle* h;
|
||||||
|
ErlNifBinary key;
|
||||||
|
ERL_NIF_TERM prev = 0;
|
||||||
|
leveldb::Status status;
|
||||||
|
|
||||||
|
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
if (!enif_inspect_binary(env, argv[1], &key)) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h->impl.alive) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
|
||||||
|
if (!it) {
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldb::Slice skey((const char*) key.data, key.size);
|
||||||
|
it->Seek(skey);
|
||||||
|
if (!it->Valid()) {
|
||||||
|
it->SeekToLast();
|
||||||
|
} else {
|
||||||
|
it->Prev();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!it->Valid()) {
|
||||||
|
delete it;
|
||||||
|
return lets_atom_end_of_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = it->value().size();
|
||||||
|
unsigned char* k = enif_make_new_binary(env, size, &prev);
|
||||||
|
if (!k) {
|
||||||
|
delete it;
|
||||||
|
return MAKEBADARG(env, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(k, it->value().data(), size);
|
||||||
|
delete it;
|
||||||
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERL_NIF_TERM
|
ERL_NIF_TERM
|
||||||
|
|
|
@ -53,10 +53,24 @@ extern "C" {
|
||||||
ERL_NIF_TERM
|
ERL_NIF_TERM
|
||||||
lets_nif_lookup2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
lets_nif_lookup2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
ERL_NIF_TERM
|
ERL_NIF_TERM
|
||||||
|
lets_nif_member2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
|
ERL_NIF_TERM
|
||||||
lets_nif_first1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
lets_nif_first1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
ERL_NIF_TERM
|
ERL_NIF_TERM
|
||||||
|
lets_nif_first_iter1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_last1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_last_iter1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
|
ERL_NIF_TERM
|
||||||
lets_nif_next2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
lets_nif_next2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
ERL_NIF_TERM
|
ERL_NIF_TERM
|
||||||
|
lets_nif_next_iter2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_prev2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
|
ERL_NIF_TERM
|
||||||
|
lets_nif_prev_iter2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
|
ERL_NIF_TERM
|
||||||
lets_nif_info_memory1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
lets_nif_info_memory1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
ERL_NIF_TERM
|
ERL_NIF_TERM
|
||||||
lets_nif_info_size1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
lets_nif_info_size1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
|
||||||
|
|
257
doc/README.md
257
doc/README.md
|
@ -9,7 +9,8 @@ __Authors:__ Joseph Wayne Norton ([`norton@alum.mit.edu`](mailto:norton@alum.mit
|
||||||
storage implementation. LETS tries to address some bad properties of
|
storage implementation. LETS tries to address some bad properties of
|
||||||
ETS and DETS. ETS is limited by physical memory. DETS is limited by
|
ETS and DETS. ETS is limited by physical memory. DETS is limited by
|
||||||
a 2 GB file size limitation and does not implement ordered sets. LETS
|
a 2 GB file size limitation and does not implement ordered sets. LETS
|
||||||
has neither of these limitations.</p>
|
has neither of these limitations. Data can also be automatically
|
||||||
|
compressed using the Snappy compression library.</p>
|
||||||
<p>For testing and comparison purposes, LETS supports three
|
<p>For testing and comparison purposes, LETS supports three
|
||||||
implementations:</p>
|
implementations:</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -30,35 +31,10 @@ implementations:</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>LETS is not intended to be an exact clone of ETS. The currently
|
<p>LETS is not intended to be an exact clone of ETS. The currently
|
||||||
supported APIs are:</p>
|
supported ETS APIs are:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<tt>new/2</tt>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>destroy/2</tt> <em>only driver and nif implementations</em>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>repair/2</tt> <em>only driver and nif implementations</em>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>insert/2</tt>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>insert_new/2</tt> <em>only the ets implementation</em>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>delete/1</tt>
|
<tt>delete/1</tt>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -69,12 +45,7 @@ supported APIs are:</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<tt>delete_all_objects/1</tt> <em>only the ets implementation</em>
|
<tt>delete_all_objects/1</tt> <em>only ets implementation</em>
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>
|
|
||||||
<tt>lookup/2</tt>
|
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -84,7 +55,17 @@ supported APIs are:</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
<tt>next/2</tt>
|
<tt>foldl/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>foldr/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>info/1</tt> <em>only a subset of items</em>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -94,6 +75,126 @@ supported APIs are:</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
|
<tt>insert/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>insert_new/2</tt> <em>only ets implementation</em>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>last/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>lookup/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>lookup_element/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match_delete/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match_object/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match_object/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>match_object/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>member/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>new/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>next/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>prev/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_count/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_delete/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_reverse/1</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_reverse/2</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<tt>select_reverse/3</tt>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
<tt>tab2list/1</tt>
|
<tt>tab2list/1</tt>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -127,7 +228,7 @@ please read further.</p>
|
||||||
|
|
||||||
<h3 id="_where_should_i_start">Where should I start?</h3>
|
<h3 id="_where_should_i_start">Where should I start?</h3>
|
||||||
<p>This README is the only bit of documentation right now.</p>
|
<p>This README is the only bit of documentation right now.</p>
|
||||||
<p>The QC (a.k.a. QuickCheck, Proper, etc.) tests underneath the
|
<p>The QC (a.k.a. QuickCheck, PropEr, etc.) tests underneath the
|
||||||
"tests/qc" directory should be helpful for understanding the
|
"tests/qc" directory should be helpful for understanding the
|
||||||
specification and behavior of ETS and LETS. These QC tests also
|
specification and behavior of ETS and LETS. These QC tests also
|
||||||
illustrate several strategies for testing Erlang Driver-based and
|
illustrate several strategies for testing Erlang Driver-based and
|
||||||
|
@ -148,6 +249,11 @@ provides an ordered mapping from string keys to string values.</p>
|
||||||
<p>See <a href="http://code.google.com/p/leveldb/">http://code.google.com/p/leveldb/</a> for further details.</p>
|
<p>See <a href="http://code.google.com/p/leveldb/">http://code.google.com/p/leveldb/</a> for further details.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 id="_what_is_snappy">What is Snappy?</h3>
|
||||||
|
<p>Snappy is a fast compression/decompression library written at Google.</p>
|
||||||
|
<p>See <a href="http://code.google.com/p/snappy/">http://code.google.com/p/snappy/</a> for further details.</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2 id="_to_download">To download</h2>
|
<h2 id="_to_download">To download</h2>
|
||||||
|
@ -415,7 +521,36 @@ Run 5,000 QuickCheck tests
|
||||||
<pre><tt>$ cd working-directory-name/src/lib/lets/.eunit
|
<pre><tt>$ cd working-directory-name/src/lib/lets/.eunit
|
||||||
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
||||||
|
|
||||||
1> qc_statem_lets:run(5000).
|
1> qc_statem_lets:qc_run(5000).
|
||||||
|
....
|
||||||
|
OK, passed 5000 tests
|
||||||
|
|
||||||
|
9.022% {delete,ok}
|
||||||
|
7.800% {new,ok}
|
||||||
|
4.535% {match_delete,ok}
|
||||||
|
4.491% {lookup,ok}
|
||||||
|
4.399% {select,ok}
|
||||||
|
4.352% {select_delete,ok}
|
||||||
|
4.348% {tab2list,ok}
|
||||||
|
4.341% {member,ok}
|
||||||
|
4.334% {last,ok}
|
||||||
|
4.315% {foldl,ok}
|
||||||
|
4.308% {select_reverse,ok}
|
||||||
|
4.301% {select_count,ok}
|
||||||
|
4.293% {select31,ok}
|
||||||
|
4.264% {first,ok}
|
||||||
|
4.216% {foldr,ok}
|
||||||
|
4.202% {match_object,ok}
|
||||||
|
4.184% {match,ok}
|
||||||
|
4.056% {insert,ok}
|
||||||
|
3.997% {prev,ok}
|
||||||
|
3.774% {next,ok}
|
||||||
|
3.416% {lookup_element,{error,badarg}}
|
||||||
|
1.298% {insert_new,ok}
|
||||||
|
0.757% {lookup_element,ok}
|
||||||
|
0.516% {next,{error,badarg}}
|
||||||
|
0.483% {prev,{error,badarg}}
|
||||||
|
true
|
||||||
.......</tt></pre>
|
.......</tt></pre>
|
||||||
|
|
||||||
|
|
||||||
|
@ -424,7 +559,7 @@ $ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
||||||
Tip
|
Tip
|
||||||
</td>
|
</td>
|
||||||
<td class="content">For testing LevelDB directly using the C bindings, try
|
<td class="content">For testing LevelDB directly using the C bindings, try
|
||||||
<tt>qc_statemc_lets:run(5000)</tt>.</td>
|
<tt>qc_statemc_lets:qc_run(5000)</tt>.</td>
|
||||||
</tr></table>
|
</tr></table>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
@ -432,12 +567,12 @@ Tip
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h2 id="_to_test_proper">To test - Proper</h2>
|
<h2 id="_to_test_proper">To test - PropEr</h2>
|
||||||
|
|
||||||
<ol class="arabic">
|
<ol class="arabic">
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
Make sure Proper is in your Erlang code path. One simple way to
|
Make sure PropEr is in your Erlang code path. One simple way to
|
||||||
accomplish this is by adding the code path to your <tt>~/.erlang</tt>
|
accomplish this is by adding the code path to your <tt>~/.erlang</tt>
|
||||||
resource file.
|
resource file.
|
||||||
</p>
|
</p>
|
||||||
|
@ -448,7 +583,7 @@ Make sure Proper is in your Erlang code path. One simple way to
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
Compile for Proper
|
Compile for PropEr
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
@ -459,14 +594,43 @@ $ make compile-proper proper-compile</tt></pre>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
Run 5,000 Proper tests
|
Run 5,000 PropEr tests
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<pre><tt>$ cd working-directory-name/src/lib/lets/.eunit
|
<pre><tt>$ cd working-directory-name/src/lib/lets/.eunit
|
||||||
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
||||||
|
|
||||||
1> qc_statem_lets:run(5000).
|
1> qc_statem_lets:qc_run(5000).
|
||||||
|
....
|
||||||
|
OK: Passed 5000 test(s).
|
||||||
|
|
||||||
|
11% {new,ok}
|
||||||
|
8% {delete,ok}
|
||||||
|
4% {member,ok}
|
||||||
|
4% {select,ok}
|
||||||
|
4% {select_count,ok}
|
||||||
|
4% {select_reverse,ok}
|
||||||
|
4% {lookup,ok}
|
||||||
|
4% {match_object,ok}
|
||||||
|
4% {tab2list,ok}
|
||||||
|
4% {last,ok}
|
||||||
|
4% {match,ok}
|
||||||
|
4% {foldl,ok}
|
||||||
|
4% {match_delete,ok}
|
||||||
|
3% {prev,ok}
|
||||||
|
3% {select31,ok}
|
||||||
|
3% {select_delete,ok}
|
||||||
|
3% {foldr,ok}
|
||||||
|
3% {insert,ok}
|
||||||
|
3% {first,ok}
|
||||||
|
3% {next,ok}
|
||||||
|
3% {lookup_element,{error,badarg}}
|
||||||
|
1% {insert_new,ok}
|
||||||
|
0% {prev,{error,badarg}}
|
||||||
|
0% {lookup_element,ok}
|
||||||
|
0% {next,{error,badarg}}
|
||||||
|
true
|
||||||
.......</tt></pre>
|
.......</tt></pre>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
@ -484,7 +648,7 @@ Documentation
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
Explain how to run QuickCheck/Proper tests using a new rebar
|
Explain how to run QuickCheck/PropEr tests using a new rebar
|
||||||
plugin.
|
plugin.
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -549,6 +713,13 @@ New APIs (TBD)
|
||||||
(<a href="http://code.google.com/p/leveldb/issues/detail?id=43">http://code.google.com/p/leveldb/issues/detail?id=43</a>)
|
(<a href="http://code.google.com/p/leveldb/issues/detail?id=43">http://code.google.com/p/leveldb/issues/detail?id=43</a>)
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
Add custom (i.e. not supported by native ETS) APIs for providing
|
||||||
|
access to LevelDB's iterators for <tt>drv</tt> and <tt>nif</tt> backend
|
||||||
|
implementations.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|
496
doc/lets.md
496
doc/lets.md
|
@ -16,6 +16,14 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###<a name="type-cont">cont()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
__abstract datatype__: `cont()`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###<a name="type-db_opts">db_opts()</a>##
|
###<a name="type-db_opts">db_opts()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,6 +64,14 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###<a name="type-item">item()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>item() = owner | name | named_table | type | keypos | protection | compressed | async | memory | size</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###<a name="type-key">key()</a>##
|
###<a name="type-key">key()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +80,30 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###<a name="type-limit">limit()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>limit() = pos_integer()</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###<a name="type-match">match()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>match() = term()</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###<a name="type-name">name()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>name() = atom()</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###<a name="type-object">object()</a>##
|
###<a name="type-object">object()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,6 +120,36 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###<a name="type-pattern">pattern()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>pattern() = atom() | tuple()</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre><tt>ets:match_pattern() is not exported!</tt></pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###<a name="type-pos">pos()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>pos() = pos_integer()</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###<a name="type-spec">spec()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>spec() = [ets:match_spec()](ets.md#type-match_spec)</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###<a name="type-tab">tab()</a>##
|
###<a name="type-tab">tab()</a>##
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,8 +164,8 @@ __abstract datatype__: `tab()`
|
||||||
guaranteed to be atomic and isolated. This function only applies
|
guaranteed to be atomic and isolated. This function only applies
|
||||||
to the <tt>ets</tt> implementation.</p>.</td></tr><tr><td valign="top"><a href="#destroy-2">destroy/2</a></td><td><p>Destroy the contents of the specified table. This function
|
to the <tt>ets</tt> implementation.</p>.</td></tr><tr><td valign="top"><a href="#destroy-2">destroy/2</a></td><td><p>Destroy the contents of the specified table. This function
|
||||||
only applies to <tt>driver</tt> and <tt>nif</tt> implementations.</p>.</td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td><p>Returns the first key <tt>Key</tt> in the table <tt>Tab</tt>. If the table
|
only applies to <tt>driver</tt> and <tt>nif</tt> implementations.</p>.</td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td><p>Returns the first key <tt>Key</tt> in the table <tt>Tab</tt>. If the table
|
||||||
is empty, <tt><em>$end_of_table</em></tt> will be returned.</p>.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td><p>Returns information about the table <tt>Tab</tt> as a list of <tt>{Item,
|
is empty, <tt><em>$end_of_table</em></tt> will be returned.</p>.</td></tr><tr><td valign="top"><a href="#foldl-3">foldl/3</a></td><td><p>Fold from left to right over the elements of the table.</p>.</td></tr><tr><td valign="top"><a href="#foldr-3">foldr/3</a></td><td><p>Fold from right to left over the elements of the table.</p>.</td></tr><tr><td valign="top"><a href="#info-1">info/1</a></td><td><p>Returns information about the table <tt>Tab</tt> as a list of <tt>{Item,
|
||||||
Value}</tt> tuples.</p>
|
Value}</tt> tuples.</p>.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td><p>Returns the information associated with <tt>Item</tt> for the table <tt>Tab</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
<pre><tt>Valid +Item+ options are:</tt></pre>
|
<pre><tt>Valid +Item+ options are:</tt></pre>
|
||||||
|
@ -152,11 +222,21 @@ is empty, <tt><em>$end_of_table</em></tt> will be returned.</p>.</td></tr><tr><t
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>.</td></tr><tr><td valign="top"><a href="#insert-2">insert/2</a></td><td><p>Inserts the object or all of the objects in the list
|
</ul>.</td></tr><tr><td valign="top"><a href="#insert-2">insert/2</a></td><td><p>Inserts the object or all of the objects in the list
|
||||||
<tt>ObjectOrObjects</tt> into the table <tt>Tab</tt>.</p>.</td></tr><tr><td valign="top"><a href="#insert_new-2">insert_new/2</a></td><td><p>This function works exactly like <tt>insert/2</tt>, with the
|
<tt>ObjOrObjs</tt> into the table <tt>Tab</tt>.</p>.</td></tr><tr><td valign="top"><a href="#insert_new-2">insert_new/2</a></td><td><p>This function works exactly like <tt>insert/2</tt>, with the
|
||||||
exception that instead of overwriting objects with the same key, it
|
exception that instead of overwriting objects with the same key, it
|
||||||
simply returns false. This function only applies to the <tt>ets</tt>
|
simply returns false. This function only applies to the <tt>ets</tt>
|
||||||
implementation.</p>.</td></tr><tr><td valign="top"><a href="#lookup-2">lookup/2</a></td><td><p>Returns a list of all objects with the key <tt>Key</tt> in the table
|
implementation.</p>.</td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td><p>Returns the last key <tt>Key</tt> in the table <tt>Tab</tt>. If the table
|
||||||
<tt>Tab</tt>.</p>.</td></tr><tr><td valign="top"><a href="#new-2">new/2</a></td><td><p>Creates a new table and returns a table identifier which can
|
is empty, <tt><em>$end_of_table</em></tt> will be returned.</p>.</td></tr><tr><td valign="top"><a href="#lookup-2">lookup/2</a></td><td><p>Returns a list of all objects with the key <tt>Key</tt> in the table
|
||||||
|
<tt>Tab</tt>.</p>.</td></tr><tr><td valign="top"><a href="#lookup_element-3">lookup_element/3</a></td><td><p>Returns the <tt>Pos</tt>:th element of the object with the key <tt>Key</tt>
|
||||||
|
in the table <tt>Tab</tt>.</p>.</td></tr><tr><td valign="top"><a href="#match-1">match/1</a></td><td><p>Continues a match started with <tt>match/3</tt>.</p>.</td></tr><tr><td valign="top"><a href="#match-2">match/2</a></td><td><p>Matches the objects in the table <tt>Tab</tt> against the pattern
|
||||||
|
<tt>Pattern</tt>.</p>.</td></tr><tr><td valign="top"><a href="#match-3">match/3</a></td><td><p>Matches the objects in the table <tt>Tab</tt> against the pattern
|
||||||
|
<tt>Pattern</tt> and returns a limited (<tt>Limit</tt>) number of matching
|
||||||
|
objects.</p>.</td></tr><tr><td valign="top"><a href="#match_delete-2">match_delete/2</a></td><td><p>Deletes all objects which match the pattern <tt>Pattern</tt> from the
|
||||||
|
table <tt>Tab</tt>.</p>.</td></tr><tr><td valign="top"><a href="#match_object-1">match_object/1</a></td><td><p>Continues a match started with <tt>match_object/3</tt>.</p>.</td></tr><tr><td valign="top"><a href="#match_object-2">match_object/2</a></td><td><p>Matches the objects in the table <tt>Tab</tt> against the pattern
|
||||||
|
<tt>Pattern</tt>.</p>.</td></tr><tr><td valign="top"><a href="#match_object-3">match_object/3</a></td><td><p>Matches the objects in the table <tt>Tab</tt> against the pattern
|
||||||
|
<tt>Pattern</tt> and returns a limited (<tt>Limit</tt>) number of matching
|
||||||
|
objects.</p>.</td></tr><tr><td valign="top"><a href="#member-2">member/2</a></td><td><p>Returns <tt>true</tt> if one or more elements in the table <tt>Tab</tt> has
|
||||||
|
the key <tt>Key</tt>, <tt>false</tt> otherwise.</p>.</td></tr><tr><td valign="top"><a href="#new-2">new/2</a></td><td><p>Creates a new table and returns a table identifier which can
|
||||||
be used in subsequent operations. The table identifier can be sent
|
be used in subsequent operations. The table identifier can be sent
|
||||||
to other processes so that a table can be shared between different
|
to other processes so that a table can be shared between different
|
||||||
processes within a node.</p>
|
processes within a node.</p>
|
||||||
|
@ -343,11 +423,20 @@ considered complete. The default is <tt>false</tt>.
|
||||||
</li>
|
</li>
|
||||||
</ul>.</td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td><p>Returns the next key <tt>Key2</tt>, following the key <tt>Key1</tt> in the
|
</ul>.</td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td><p>Returns the next key <tt>Key2</tt>, following the key <tt>Key1</tt> in the
|
||||||
table <tt>Tab</tt>. If there is no next key, <tt><em>$end_of_table</em></tt> is
|
table <tt>Tab</tt>. If there is no next key, <tt><em>$end_of_table</em></tt> is
|
||||||
|
returned.</p>.</td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td><p>Returns the previous key <tt>Key2</tt>, following the key <tt>Key1</tt> in
|
||||||
|
the table <tt>Tab</tt>. If there is no previous key, <tt><em>$end_of_table</em></tt> is
|
||||||
returned.</p>.</td></tr><tr><td valign="top"><a href="#repair-2">repair/2</a></td><td><p>If a table cannot be opened, you may attempt to call this
|
returned.</p>.</td></tr><tr><td valign="top"><a href="#repair-2">repair/2</a></td><td><p>If a table cannot be opened, you may attempt to call this
|
||||||
method to resurrect as much of the contents of the table as
|
method to resurrect as much of the contents of the table as
|
||||||
possible. Some data may be lost, so be careful when calling this
|
possible. Some data may be lost, so be careful when calling this
|
||||||
function on a table that contains important information. This
|
function on a table that contains important information. This
|
||||||
function only applies to <tt>driver</tt> and <tt>nif</tt> implementations.</p>.</td></tr><tr><td valign="top"><a href="#tab2list-1">tab2list/1</a></td><td><p>Returns a list of all objects in the table <tt>Tab</tt>. The
|
function only applies to <tt>driver</tt> and <tt>nif</tt> implementations.</p>.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td><p>Continues a select started with <tt>select/3</tt>.</p>.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td><p>Matches the objects in the table <tt>Tab</tt> against the spec
|
||||||
|
<tt>Spec</tt>.</p>.</td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td><p>Matches the objects in the table <tt>Tab</tt> against the spec <tt>Spec</tt>
|
||||||
|
and returns a limited (<tt>Limit</tt>) number of matching objects.</p>.</td></tr><tr><td valign="top"><a href="#select_count-2">select_count/2</a></td><td><p>Counts all objects which match the spec <tt>Spec</tt> from the
|
||||||
|
table <tt>Tab</tt> and returns the number matched.</p>.</td></tr><tr><td valign="top"><a href="#select_delete-2">select_delete/2</a></td><td><p>Deletes all objects which match the spec <tt>Spec</tt> from the
|
||||||
|
table <tt>Tab</tt> and returns the number deleted.</p>.</td></tr><tr><td valign="top"><a href="#select_reverse-1">select_reverse/1</a></td><td><p>Continues a select reverse started with <tt>select_reverse/3</tt>.</p>.</td></tr><tr><td valign="top"><a href="#select_reverse-2">select_reverse/2</a></td><td><p>Matches in reverse the objects in the table <tt>Tab</tt> against the
|
||||||
|
spec <tt>Spec</tt>.</p>.</td></tr><tr><td valign="top"><a href="#select_reverse-3">select_reverse/3</a></td><td><p>Matches in reverse the objects in the table <tt>Tab</tt> against the
|
||||||
|
spec <tt>Spec</tt> and returns a limited (<tt>Limit</tt>) number of matching
|
||||||
|
objects.</p>.</td></tr><tr><td valign="top"><a href="#tab2list-1">tab2list/1</a></td><td><p>Returns a list of all objects in the table <tt>Tab</tt>. The
|
||||||
operation is <strong>not</strong> guaranteed to be atomic and isolated.</p>.</td></tr></table>
|
operation is <strong>not</strong> guaranteed to be atomic and isolated.</p>.</td></tr></table>
|
||||||
|
|
||||||
|
|
||||||
|
@ -412,7 +501,7 @@ __See also:__ [ets:delete_all_objects/1](ets.md#delete_all_objects-1).<a name="d
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<pre>destroy(Name::atom(), Options::[opts()](#type-opts)) -> true</pre>
|
<pre>destroy(Name::[name()](#type-name), Opts::[opts()](#type-opts)) -> true</pre>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
@ -437,14 +526,44 @@ only applies to <tt>driver</tt> and <tt>nif</tt> implementations.</p>
|
||||||
is empty, <tt><em>$end_of_table</em></tt> will be returned.</p>
|
is empty, <tt><em>$end_of_table</em></tt> will be returned.</p>
|
||||||
|
|
||||||
|
|
||||||
__See also:__ [ets:first/1](ets.md#first-1).<a name="info-2"></a>
|
__See also:__ [ets:first/1](ets.md#first-1).<a name="foldl-3"></a>
|
||||||
|
|
||||||
###info/2##
|
###foldl/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<pre>info(Tab::[tab()](#type-tab), Item::atom()) -> term()</pre>
|
<pre>foldl(Fun, Acc0::term(), Tab::[tab()](#type-tab)) -> Acc1::term()</pre>
|
||||||
|
<ul class="definitions"><li><pre>Fun = fun((Element::term(), AccIn::term()) -> AccOut::term())</pre></li></ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Fold from left to right over the elements of the table.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:foldl/3](ets.md#foldl-3).<a name="foldr-3"></a>
|
||||||
|
|
||||||
|
###foldr/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>foldr(Fun, Acc0::term(), Tab::[tab()](#type-tab)) -> Acc1::term()</pre>
|
||||||
|
<ul class="definitions"><li><pre>Fun = fun((Element::term(), AccIn::term()) -> AccOut::term())</pre></li></ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Fold from right to left over the elements of the table.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:foldr/3](ets.md#foldr-3).<a name="info-1"></a>
|
||||||
|
|
||||||
|
###info/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>info(Tab::[tab()](#type-tab)) -> [{[item()](#type-item), term()}]</pre>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
@ -454,6 +573,22 @@ __See also:__ [ets:first/1](ets.md#first-1).<a name="info-2"></a>
|
||||||
Value}</tt> tuples.</p>
|
Value}</tt> tuples.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [info/2](#info-2).<a name="info-2"></a>
|
||||||
|
|
||||||
|
###info/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>info(Tab::[tab()](#type-tab), Item::[item()](#type-item)) -> term()</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Returns the information associated with <tt>Item</tt> for the table <tt>Tab</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
<pre><tt>Valid +Item+ options are:</tt></pre>
|
<pre><tt>Valid +Item+ options are:</tt></pre>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -517,14 +652,14 @@ __See also:__ [ets:info/2](ets.md#info-2).<a name="insert-2"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<pre>insert(Tab::[tab()](#type-tab), ObjectOrObjects::[object()](#type-object) | [[object()](#type-object)]) -> true</pre>
|
<pre>insert(Tab::[tab()](#type-tab), ObjOrObjs::[object()](#type-object) | [[object()](#type-object)]) -> true</pre>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p>Inserts the object or all of the objects in the list
|
<p>Inserts the object or all of the objects in the list
|
||||||
<tt>ObjectOrObjects</tt> into the table <tt>Tab</tt>.</p>
|
<tt>ObjOrObjs</tt> into the table <tt>Tab</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
__See also:__ [ets:insert/2](ets.md#insert-2).<a name="insert_new-2"></a>
|
__See also:__ [ets:insert/2](ets.md#insert-2).<a name="insert_new-2"></a>
|
||||||
|
@ -534,7 +669,7 @@ __See also:__ [ets:insert/2](ets.md#insert-2).<a name="insert_new-2"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<pre>insert_new(Tab::[tab()](#type-tab), ObjectOrObjects::[object()](#type-object) | [[object()](#type-object)]) -> true</pre>
|
<pre>insert_new(Tab::[tab()](#type-tab), ObjOrObjs::[object()](#type-object) | [[object()](#type-object)]) -> true</pre>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
@ -546,7 +681,24 @@ simply returns false. This function only applies to the <tt>ets</tt>
|
||||||
implementation.</p>
|
implementation.</p>
|
||||||
|
|
||||||
|
|
||||||
__See also:__ [ets:insert_new/2](ets.md#insert_new-2).<a name="lookup-2"></a>
|
__See also:__ [ets:insert_new/2](ets.md#insert_new-2).<a name="last-1"></a>
|
||||||
|
|
||||||
|
###last/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>last(Tab::[tab()](#type-tab)) -> [key()](#type-key) | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Returns the last key <tt>Key</tt> in the table <tt>Tab</tt>. If the table
|
||||||
|
is empty, <tt><em>$end_of_table</em></tt> will be returned.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:last/1](ets.md#last-1).<a name="lookup-2"></a>
|
||||||
|
|
||||||
###lookup/2##
|
###lookup/2##
|
||||||
|
|
||||||
|
@ -563,14 +715,167 @@ __See also:__ [ets:insert_new/2](ets.md#insert_new-2).<a name="lookup-2"></a>
|
||||||
<tt>Tab</tt>.</p>
|
<tt>Tab</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
__See also:__ [ets:lookup/2](ets.md#lookup-2).<a name="new-2"></a>
|
__See also:__ [ets:lookup/2](ets.md#lookup-2).<a name="lookup_element-3"></a>
|
||||||
|
|
||||||
|
###lookup_element/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>lookup_element(Tab::[tab()](#type-tab), Key::[key()](#type-key), Pos::[pos()](#type-pos)) -> term()</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Returns the <tt>Pos</tt>:th element of the object with the key <tt>Key</tt>
|
||||||
|
in the table <tt>Tab</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:lookup_element/3](ets.md#lookup_element-3).<a name="match-1"></a>
|
||||||
|
|
||||||
|
###match/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>match(X1::[cont()](#type-cont)) -> {[[match()](#type-match)], [cont()](#type-cont)} | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Continues a match started with <tt>match/3</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:match/1](ets.md#match-1).<a name="match-2"></a>
|
||||||
|
|
||||||
|
###match/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>match(Tab::[tab()](#type-tab), Pattern::[pattern()](#type-pattern)) -> [[match()](#type-match)]</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Matches the objects in the table <tt>Tab</tt> against the pattern
|
||||||
|
<tt>Pattern</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:match/2](ets.md#match-2).<a name="match-3"></a>
|
||||||
|
|
||||||
|
###match/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>match(Tab::[tab()](#type-tab), Pattern::[pattern()](#type-pattern), Limit::[limit()](#type-limit)) -> {[[match()](#type-match)], [cont()](#type-cont)} | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Matches the objects in the table <tt>Tab</tt> against the pattern
|
||||||
|
<tt>Pattern</tt> and returns a limited (<tt>Limit</tt>) number of matching
|
||||||
|
objects.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:match/3](ets.md#match-3).<a name="match_delete-2"></a>
|
||||||
|
|
||||||
|
###match_delete/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>match_delete(Tab::[tab()](#type-tab), Pattern::[pattern()](#type-pattern)) -> true</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Deletes all objects which match the pattern <tt>Pattern</tt> from the
|
||||||
|
table <tt>Tab</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:match_delete/2](ets.md#match_delete-2).<a name="match_object-1"></a>
|
||||||
|
|
||||||
|
###match_object/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>match_object(X1::[cont()](#type-cont)) -> {[[match()](#type-match)], [cont()](#type-cont)} | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Continues a match started with <tt>match_object/3</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:match_object/1](ets.md#match_object-1).<a name="match_object-2"></a>
|
||||||
|
|
||||||
|
###match_object/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>match_object(Tab::[tab()](#type-tab), Pattern::[pattern()](#type-pattern)) -> [[match()](#type-match)]</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Matches the objects in the table <tt>Tab</tt> against the pattern
|
||||||
|
<tt>Pattern</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:match_object/2](ets.md#match_object-2).<a name="match_object-3"></a>
|
||||||
|
|
||||||
|
###match_object/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>match_object(Tab::[tab()](#type-tab), Pattern::[pattern()](#type-pattern), Limit::[limit()](#type-limit)) -> {[[match()](#type-match)], [cont()](#type-cont)} | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Matches the objects in the table <tt>Tab</tt> against the pattern
|
||||||
|
<tt>Pattern</tt> and returns a limited (<tt>Limit</tt>) number of matching
|
||||||
|
objects.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:match_object/3](ets.md#match_object-3).<a name="member-2"></a>
|
||||||
|
|
||||||
|
###member/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>member(Tab::[tab()](#type-tab), Key::[key()](#type-key)) -> true | false</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Returns <tt>true</tt> if one or more elements in the table <tt>Tab</tt> has
|
||||||
|
the key <tt>Key</tt>, <tt>false</tt> otherwise.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:member/2](ets.md#member-2).<a name="new-2"></a>
|
||||||
|
|
||||||
###new/2##
|
###new/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<pre>new(Name::atom(), Options::[opts()](#type-opts)) -> [tab()](#type-tab)</pre>
|
<pre>new(Name::[name()](#type-name), Opts::[opts()](#type-opts)) -> [tab()](#type-tab)</pre>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
@ -782,14 +1087,32 @@ table <tt>Tab</tt>. If there is no next key, <tt><em>$end_of_table</em></tt> is
|
||||||
returned.</p>
|
returned.</p>
|
||||||
|
|
||||||
|
|
||||||
__See also:__ [ets:next/2](ets.md#next-2).<a name="repair-2"></a>
|
__See also:__ [ets:next/2](ets.md#next-2).<a name="prev-2"></a>
|
||||||
|
|
||||||
|
###prev/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>prev(Tab::[tab()](#type-tab), Key::[key()](#type-key)) -> [key()](#type-key) | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Returns the previous key <tt>Key2</tt>, following the key <tt>Key1</tt> in
|
||||||
|
the table <tt>Tab</tt>. If there is no previous key, <tt><em>$end_of_table</em></tt> is
|
||||||
|
returned.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:prev/2](ets.md#prev-2).<a name="repair-2"></a>
|
||||||
|
|
||||||
###repair/2##
|
###repair/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<pre>repair(Name::atom(), Options::[opts()](#type-opts)) -> true</pre>
|
<pre>repair(Name::[name()](#type-name), Opts::[opts()](#type-opts)) -> true</pre>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
@ -800,7 +1123,142 @@ method to resurrect as much of the contents of the table as
|
||||||
possible. Some data may be lost, so be careful when calling this
|
possible. Some data may be lost, so be careful when calling this
|
||||||
function on a table that contains important information. This
|
function on a table that contains important information. This
|
||||||
function only applies to <tt>driver</tt> and <tt>nif</tt> implementations.</p>
|
function only applies to <tt>driver</tt> and <tt>nif</tt> implementations.</p>
|
||||||
<a name="tab2list-1"></a>
|
<a name="select-1"></a>
|
||||||
|
|
||||||
|
###select/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>select(X1::[cont()](#type-cont)) -> {[[match()](#type-match)], [cont()](#type-cont)} | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Continues a select started with <tt>select/3</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:select/1](ets.md#select-1).<a name="select-2"></a>
|
||||||
|
|
||||||
|
###select/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>select(Tab::[tab()](#type-tab), Spec::[spec()](#type-spec)) -> [[match()](#type-match)]</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Matches the objects in the table <tt>Tab</tt> against the spec
|
||||||
|
<tt>Spec</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:select/2](ets.md#select-2).<a name="select-3"></a>
|
||||||
|
|
||||||
|
###select/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>select(Tab::[tab()](#type-tab), Spec::[spec()](#type-spec), Limit::[limit()](#type-limit)) -> {[[match()](#type-match)], [cont()](#type-cont)} | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Matches the objects in the table <tt>Tab</tt> against the spec <tt>Spec</tt>
|
||||||
|
and returns a limited (<tt>Limit</tt>) number of matching objects.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:select/3](ets.md#select-3).<a name="select_count-2"></a>
|
||||||
|
|
||||||
|
###select_count/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>select_count(Tab::[tab()](#type-tab), Spec::[pattern()](#type-pattern)) -> pos_integer()</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Counts all objects which match the spec <tt>Spec</tt> from the
|
||||||
|
table <tt>Tab</tt> and returns the number matched.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:select_count/2](ets.md#select_count-2).<a name="select_delete-2"></a>
|
||||||
|
|
||||||
|
###select_delete/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>select_delete(Tab::[tab()](#type-tab), Spec::[pattern()](#type-pattern)) -> pos_integer()</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Deletes all objects which match the spec <tt>Spec</tt> from the
|
||||||
|
table <tt>Tab</tt> and returns the number deleted.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:select_delete/2](ets.md#select_delete-2).<a name="select_reverse-1"></a>
|
||||||
|
|
||||||
|
###select_reverse/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>select_reverse(X1::[cont()](#type-cont)) -> {[[match()](#type-match)], [cont()](#type-cont)} | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Continues a select reverse started with <tt>select_reverse/3</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:select_reverse/1](ets.md#select_reverse-1).<a name="select_reverse-2"></a>
|
||||||
|
|
||||||
|
###select_reverse/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>select_reverse(Tab::[tab()](#type-tab), Spec::[spec()](#type-spec)) -> [[match()](#type-match)]</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Matches in reverse the objects in the table <tt>Tab</tt> against the
|
||||||
|
spec <tt>Spec</tt>.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:select_reverse/2](ets.md#select_reverse-2).<a name="select_reverse-3"></a>
|
||||||
|
|
||||||
|
###select_reverse/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<pre>select_reverse(Tab::[tab()](#type-tab), Spec::[spec()](#type-spec), Limit::[limit()](#type-limit)) -> {[[match()](#type-match)], [cont()](#type-cont)} | '$end_of_table'</pre>
|
||||||
|
<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Matches in reverse the objects in the table <tt>Tab</tt> against the
|
||||||
|
spec <tt>Spec</tt> and returns a limited (<tt>Limit</tt>) number of matching
|
||||||
|
objects.</p>
|
||||||
|
|
||||||
|
|
||||||
|
__See also:__ [ets:select_reverse/3](ets.md#select_reverse-3).<a name="tab2list-1"></a>
|
||||||
|
|
||||||
###tab2list/1##
|
###tab2list/1##
|
||||||
|
|
||||||
|
|
265
doc/lets_drv.md
265
doc/lets_drv.md
|
@ -12,13 +12,22 @@
|
||||||
##Function Index##
|
##Function Index##
|
||||||
|
|
||||||
|
|
||||||
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#delete-2">delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#delete-3">delete/3</a></td><td></td></tr><tr><td valign="top"><a href="#delete_all_objects-2">delete_all_objects/2</a></td><td></td></tr><tr><td valign="top"><a href="#destroy-4">destroy/4</a></td><td></td></tr><tr><td valign="top"><a href="#first-2">first/2</a></td><td></td></tr><tr><td valign="top"><a href="#info_memory-2">info_memory/2</a></td><td></td></tr><tr><td valign="top"><a href="#info_size-2">info_size/2</a></td><td></td></tr><tr><td valign="top"><a href="#insert-3">insert/3</a></td><td></td></tr><tr><td valign="top"><a href="#insert_new-3">insert_new/3</a></td><td></td></tr><tr><td valign="top"><a href="#lookup-3">lookup/3</a></td><td></td></tr><tr><td valign="top"><a href="#next-3">next/3</a></td><td></td></tr><tr><td valign="top"><a href="#open-4">open/4</a></td><td></td></tr><tr><td valign="top"><a href="#repair-4">repair/4</a></td><td></td></tr><tr><td valign="top"><a href="#tab2list-2">tab2list/2</a></td><td></td></tr></table>
|
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#delete-1">delete/1</a></td><td></td></tr><tr><td valign="top"><a href="#delete-2">delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#delete_all_objects-1">delete_all_objects/1</a></td><td></td></tr><tr><td valign="top"><a href="#destroy-4">destroy/4</a></td><td></td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td></td></tr><tr><td valign="top"><a href="#foldl-3">foldl/3</a></td><td></td></tr><tr><td valign="top"><a href="#foldr-3">foldr/3</a></td><td></td></tr><tr><td valign="top"><a href="#info_memory-1">info_memory/1</a></td><td></td></tr><tr><td valign="top"><a href="#info_size-1">info_size/1</a></td><td></td></tr><tr><td valign="top"><a href="#insert-2">insert/2</a></td><td></td></tr><tr><td valign="top"><a href="#insert_new-2">insert_new/2</a></td><td></td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td></td></tr><tr><td valign="top"><a href="#lookup-2">lookup/2</a></td><td></td></tr><tr><td valign="top"><a href="#lookup_element-3">lookup_element/3</a></td><td></td></tr><tr><td valign="top"><a href="#match-1">match/1</a></td><td></td></tr><tr><td valign="top"><a href="#match-2">match/2</a></td><td></td></tr><tr><td valign="top"><a href="#match-3">match/3</a></td><td></td></tr><tr><td valign="top"><a href="#match_delete-2">match_delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#match_object-1">match_object/1</a></td><td></td></tr><tr><td valign="top"><a href="#match_object-2">match_object/2</a></td><td></td></tr><tr><td valign="top"><a href="#match_object-3">match_object/3</a></td><td></td></tr><tr><td valign="top"><a href="#member-2">member/2</a></td><td></td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td></td></tr><tr><td valign="top"><a href="#open-4">open/4</a></td><td></td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td></td></tr><tr><td valign="top"><a href="#repair-4">repair/4</a></td><td></td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td></td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td></td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td></td></tr><tr><td valign="top"><a href="#select_count-2">select_count/2</a></td><td></td></tr><tr><td valign="top"><a href="#select_delete-2">select_delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#select_reverse-1">select_reverse/1</a></td><td></td></tr><tr><td valign="top"><a href="#select_reverse-2">select_reverse/2</a></td><td></td></tr><tr><td valign="top"><a href="#select_reverse-3">select_reverse/3</a></td><td></td></tr><tr><td valign="top"><a href="#tab2list-1">tab2list/1</a></td><td></td></tr></table>
|
||||||
|
|
||||||
|
|
||||||
<a name="functions"></a>
|
<a name="functions"></a>
|
||||||
|
|
||||||
##Function Details##
|
##Function Details##
|
||||||
|
|
||||||
|
<a name="delete-1"></a>
|
||||||
|
|
||||||
|
###delete/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`delete(Tab) -> any()`
|
||||||
|
|
||||||
<a name="delete-2"></a>
|
<a name="delete-2"></a>
|
||||||
|
|
||||||
###delete/2##
|
###delete/2##
|
||||||
|
@ -26,25 +35,16 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`delete(Tab, Impl) -> any()`
|
`delete(Tab, Key) -> any()`
|
||||||
|
|
||||||
<a name="delete-3"></a>
|
<a name="delete_all_objects-1"></a>
|
||||||
|
|
||||||
###delete/3##
|
###delete_all_objects/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`delete(Tab, Impl, Key) -> any()`
|
`delete_all_objects(Tab) -> any()`
|
||||||
|
|
||||||
<a name="delete_all_objects-2"></a>
|
|
||||||
|
|
||||||
###delete_all_objects/2##
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`delete_all_objects(Tab, Impl) -> any()`
|
|
||||||
|
|
||||||
<a name="destroy-4"></a>
|
<a name="destroy-4"></a>
|
||||||
|
|
||||||
|
@ -55,68 +55,176 @@
|
||||||
|
|
||||||
`destroy(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
`destroy(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
||||||
|
|
||||||
<a name="first-2"></a>
|
<a name="first-1"></a>
|
||||||
|
|
||||||
###first/2##
|
###first/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`first(Tab, Impl) -> any()`
|
`first(Tab) -> any()`
|
||||||
|
|
||||||
<a name="info_memory-2"></a>
|
<a name="foldl-3"></a>
|
||||||
|
|
||||||
###info_memory/2##
|
###foldl/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`info_memory(Tab, Impl) -> any()`
|
`foldl(Fun, Acc0, Tab) -> any()`
|
||||||
|
|
||||||
<a name="info_size-2"></a>
|
<a name="foldr-3"></a>
|
||||||
|
|
||||||
###info_size/2##
|
###foldr/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`info_size(Tab, Impl) -> any()`
|
`foldr(Fun, Acc0, Tab) -> any()`
|
||||||
|
|
||||||
<a name="insert-3"></a>
|
<a name="info_memory-1"></a>
|
||||||
|
|
||||||
###insert/3##
|
###info_memory/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`insert(Tab, Impl, Object) -> any()`
|
`info_memory(Tab) -> any()`
|
||||||
|
|
||||||
<a name="insert_new-3"></a>
|
<a name="info_size-1"></a>
|
||||||
|
|
||||||
###insert_new/3##
|
###info_size/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`insert_new(Tab, Impl, Object) -> any()`
|
`info_size(Tab) -> any()`
|
||||||
|
|
||||||
<a name="lookup-3"></a>
|
<a name="insert-2"></a>
|
||||||
|
|
||||||
###lookup/3##
|
###insert/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`lookup(Tab, Impl, Key) -> any()`
|
`insert(Tab, Object) -> any()`
|
||||||
|
|
||||||
<a name="next-3"></a>
|
<a name="insert_new-2"></a>
|
||||||
|
|
||||||
###next/3##
|
###insert_new/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`next(Tab, Impl, Key) -> any()`
|
`insert_new(Tab, Object) -> any()`
|
||||||
|
|
||||||
|
<a name="last-1"></a>
|
||||||
|
|
||||||
|
###last/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`last(Tab) -> any()`
|
||||||
|
|
||||||
|
<a name="lookup-2"></a>
|
||||||
|
|
||||||
|
###lookup/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`lookup(Tab, Key) -> any()`
|
||||||
|
|
||||||
|
<a name="lookup_element-3"></a>
|
||||||
|
|
||||||
|
###lookup_element/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`lookup_element(Tab, Key, Pos) -> any()`
|
||||||
|
|
||||||
|
<a name="match-1"></a>
|
||||||
|
|
||||||
|
###match/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match(Cont) -> any()`
|
||||||
|
|
||||||
|
<a name="match-2"></a>
|
||||||
|
|
||||||
|
###match/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match(Tab, Pattern) -> any()`
|
||||||
|
|
||||||
|
<a name="match-3"></a>
|
||||||
|
|
||||||
|
###match/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match(Tab, Pattern, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="match_delete-2"></a>
|
||||||
|
|
||||||
|
###match_delete/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_delete(Tab, Pattern) -> any()`
|
||||||
|
|
||||||
|
<a name="match_object-1"></a>
|
||||||
|
|
||||||
|
###match_object/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_object(Cont) -> any()`
|
||||||
|
|
||||||
|
<a name="match_object-2"></a>
|
||||||
|
|
||||||
|
###match_object/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_object(Tab, Pattern) -> any()`
|
||||||
|
|
||||||
|
<a name="match_object-3"></a>
|
||||||
|
|
||||||
|
###match_object/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_object(Tab, Pattern, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="member-2"></a>
|
||||||
|
|
||||||
|
###member/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`member(Tab, Key) -> any()`
|
||||||
|
|
||||||
|
<a name="next-2"></a>
|
||||||
|
|
||||||
|
###next/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`next(Tab, Key) -> any()`
|
||||||
|
|
||||||
<a name="open-4"></a>
|
<a name="open-4"></a>
|
||||||
|
|
||||||
|
@ -127,6 +235,15 @@
|
||||||
|
|
||||||
`open(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
`open(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
||||||
|
|
||||||
|
<a name="prev-2"></a>
|
||||||
|
|
||||||
|
###prev/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`prev(Tab, Key) -> any()`
|
||||||
|
|
||||||
<a name="repair-4"></a>
|
<a name="repair-4"></a>
|
||||||
|
|
||||||
###repair/4##
|
###repair/4##
|
||||||
|
@ -136,12 +253,84 @@
|
||||||
|
|
||||||
`repair(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
`repair(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
||||||
|
|
||||||
<a name="tab2list-2"></a>
|
<a name="select-1"></a>
|
||||||
|
|
||||||
###tab2list/2##
|
###select/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`tab2list(Tab, Impl) -> any()`
|
`select(Cont0) -> any()`
|
||||||
|
|
||||||
|
<a name="select-2"></a>
|
||||||
|
|
||||||
|
###select/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select-3"></a>
|
||||||
|
|
||||||
|
###select/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select(Tab, Spec, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="select_count-2"></a>
|
||||||
|
|
||||||
|
###select_count/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_count(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select_delete-2"></a>
|
||||||
|
|
||||||
|
###select_delete/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_delete(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select_reverse-1"></a>
|
||||||
|
|
||||||
|
###select_reverse/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_reverse(Cont0) -> any()`
|
||||||
|
|
||||||
|
<a name="select_reverse-2"></a>
|
||||||
|
|
||||||
|
###select_reverse/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_reverse(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select_reverse-3"></a>
|
||||||
|
|
||||||
|
###select_reverse/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_reverse(Tab, Spec, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="tab2list-1"></a>
|
||||||
|
|
||||||
|
###tab2list/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`tab2list(Tab) -> any()`
|
||||||
|
|
||||||
|
|
265
doc/lets_ets.md
265
doc/lets_ets.md
|
@ -12,13 +12,22 @@
|
||||||
##Function Index##
|
##Function Index##
|
||||||
|
|
||||||
|
|
||||||
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#delete-2">delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#delete-3">delete/3</a></td><td></td></tr><tr><td valign="top"><a href="#delete_all_objects-2">delete_all_objects/2</a></td><td></td></tr><tr><td valign="top"><a href="#destroy-1">destroy/1</a></td><td></td></tr><tr><td valign="top"><a href="#first-2">first/2</a></td><td></td></tr><tr><td valign="top"><a href="#info_memory-2">info_memory/2</a></td><td></td></tr><tr><td valign="top"><a href="#info_size-2">info_size/2</a></td><td></td></tr><tr><td valign="top"><a href="#insert-3">insert/3</a></td><td></td></tr><tr><td valign="top"><a href="#insert_new-3">insert_new/3</a></td><td></td></tr><tr><td valign="top"><a href="#lookup-3">lookup/3</a></td><td></td></tr><tr><td valign="top"><a href="#next-3">next/3</a></td><td></td></tr><tr><td valign="top"><a href="#open-1">open/1</a></td><td></td></tr><tr><td valign="top"><a href="#repair-1">repair/1</a></td><td></td></tr><tr><td valign="top"><a href="#tab2list-2">tab2list/2</a></td><td></td></tr></table>
|
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#delete-1">delete/1</a></td><td></td></tr><tr><td valign="top"><a href="#delete-2">delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#delete_all_objects-1">delete_all_objects/1</a></td><td></td></tr><tr><td valign="top"><a href="#destroy-1">destroy/1</a></td><td></td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td></td></tr><tr><td valign="top"><a href="#foldl-3">foldl/3</a></td><td></td></tr><tr><td valign="top"><a href="#foldr-3">foldr/3</a></td><td></td></tr><tr><td valign="top"><a href="#info_memory-1">info_memory/1</a></td><td></td></tr><tr><td valign="top"><a href="#info_size-1">info_size/1</a></td><td></td></tr><tr><td valign="top"><a href="#insert-2">insert/2</a></td><td></td></tr><tr><td valign="top"><a href="#insert_new-2">insert_new/2</a></td><td></td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td></td></tr><tr><td valign="top"><a href="#lookup-2">lookup/2</a></td><td></td></tr><tr><td valign="top"><a href="#lookup_element-3">lookup_element/3</a></td><td></td></tr><tr><td valign="top"><a href="#match-1">match/1</a></td><td></td></tr><tr><td valign="top"><a href="#match-2">match/2</a></td><td></td></tr><tr><td valign="top"><a href="#match-3">match/3</a></td><td></td></tr><tr><td valign="top"><a href="#match_delete-2">match_delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#match_object-1">match_object/1</a></td><td></td></tr><tr><td valign="top"><a href="#match_object-2">match_object/2</a></td><td></td></tr><tr><td valign="top"><a href="#match_object-3">match_object/3</a></td><td></td></tr><tr><td valign="top"><a href="#member-2">member/2</a></td><td></td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td></td></tr><tr><td valign="top"><a href="#open-1">open/1</a></td><td></td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td></td></tr><tr><td valign="top"><a href="#repair-1">repair/1</a></td><td></td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td></td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td></td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td></td></tr><tr><td valign="top"><a href="#select_count-2">select_count/2</a></td><td></td></tr><tr><td valign="top"><a href="#select_delete-2">select_delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#select_reverse-1">select_reverse/1</a></td><td></td></tr><tr><td valign="top"><a href="#select_reverse-2">select_reverse/2</a></td><td></td></tr><tr><td valign="top"><a href="#select_reverse-3">select_reverse/3</a></td><td></td></tr><tr><td valign="top"><a href="#tab2list-1">tab2list/1</a></td><td></td></tr></table>
|
||||||
|
|
||||||
|
|
||||||
<a name="functions"></a>
|
<a name="functions"></a>
|
||||||
|
|
||||||
##Function Details##
|
##Function Details##
|
||||||
|
|
||||||
|
<a name="delete-1"></a>
|
||||||
|
|
||||||
|
###delete/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`delete(Tab) -> any()`
|
||||||
|
|
||||||
<a name="delete-2"></a>
|
<a name="delete-2"></a>
|
||||||
|
|
||||||
###delete/2##
|
###delete/2##
|
||||||
|
@ -26,25 +35,16 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`delete(Tab, Ets) -> any()`
|
`delete(Tab, Key) -> any()`
|
||||||
|
|
||||||
<a name="delete-3"></a>
|
<a name="delete_all_objects-1"></a>
|
||||||
|
|
||||||
###delete/3##
|
###delete_all_objects/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`delete(Tab, Ets, Key) -> any()`
|
`delete_all_objects(Tab) -> any()`
|
||||||
|
|
||||||
<a name="delete_all_objects-2"></a>
|
|
||||||
|
|
||||||
###delete_all_objects/2##
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`delete_all_objects(Tab, Ets) -> any()`
|
|
||||||
|
|
||||||
<a name="destroy-1"></a>
|
<a name="destroy-1"></a>
|
||||||
|
|
||||||
|
@ -55,68 +55,176 @@
|
||||||
|
|
||||||
`destroy(Tab) -> any()`
|
`destroy(Tab) -> any()`
|
||||||
|
|
||||||
<a name="first-2"></a>
|
<a name="first-1"></a>
|
||||||
|
|
||||||
###first/2##
|
###first/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`first(Tab, Ets) -> any()`
|
`first(Tab) -> any()`
|
||||||
|
|
||||||
<a name="info_memory-2"></a>
|
<a name="foldl-3"></a>
|
||||||
|
|
||||||
###info_memory/2##
|
###foldl/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`info_memory(Tab, Ets) -> any()`
|
`foldl(Function, Acc0, Tab) -> any()`
|
||||||
|
|
||||||
<a name="info_size-2"></a>
|
<a name="foldr-3"></a>
|
||||||
|
|
||||||
###info_size/2##
|
###foldr/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`info_size(Tab, Ets) -> any()`
|
`foldr(Function, Acc0, Tab) -> any()`
|
||||||
|
|
||||||
<a name="insert-3"></a>
|
<a name="info_memory-1"></a>
|
||||||
|
|
||||||
###insert/3##
|
###info_memory/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`insert(Tab, Ets, ObjectOrObjects) -> any()`
|
`info_memory(Tab) -> any()`
|
||||||
|
|
||||||
<a name="insert_new-3"></a>
|
<a name="info_size-1"></a>
|
||||||
|
|
||||||
###insert_new/3##
|
###info_size/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`insert_new(Tab, Ets, ObjectOrObjects) -> any()`
|
`info_size(Tab) -> any()`
|
||||||
|
|
||||||
<a name="lookup-3"></a>
|
<a name="insert-2"></a>
|
||||||
|
|
||||||
###lookup/3##
|
###insert/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`lookup(Tab, Ets, Key) -> any()`
|
`insert(Tab, ObjOrObjs) -> any()`
|
||||||
|
|
||||||
<a name="next-3"></a>
|
<a name="insert_new-2"></a>
|
||||||
|
|
||||||
###next/3##
|
###insert_new/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`next(Tab, Ets, Key) -> any()`
|
`insert_new(Tab, ObjOrObjs) -> any()`
|
||||||
|
|
||||||
|
<a name="last-1"></a>
|
||||||
|
|
||||||
|
###last/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`last(Tab) -> any()`
|
||||||
|
|
||||||
|
<a name="lookup-2"></a>
|
||||||
|
|
||||||
|
###lookup/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`lookup(Tab, Key) -> any()`
|
||||||
|
|
||||||
|
<a name="lookup_element-3"></a>
|
||||||
|
|
||||||
|
###lookup_element/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`lookup_element(Tab, Key, Pos) -> any()`
|
||||||
|
|
||||||
|
<a name="match-1"></a>
|
||||||
|
|
||||||
|
###match/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match(Cont) -> any()`
|
||||||
|
|
||||||
|
<a name="match-2"></a>
|
||||||
|
|
||||||
|
###match/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match(Tab, Pattern) -> any()`
|
||||||
|
|
||||||
|
<a name="match-3"></a>
|
||||||
|
|
||||||
|
###match/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match(Tab, Pattern, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="match_delete-2"></a>
|
||||||
|
|
||||||
|
###match_delete/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_delete(Tab, Pattern) -> any()`
|
||||||
|
|
||||||
|
<a name="match_object-1"></a>
|
||||||
|
|
||||||
|
###match_object/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_object(Cont) -> any()`
|
||||||
|
|
||||||
|
<a name="match_object-2"></a>
|
||||||
|
|
||||||
|
###match_object/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_object(Tab, Pattern) -> any()`
|
||||||
|
|
||||||
|
<a name="match_object-3"></a>
|
||||||
|
|
||||||
|
###match_object/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_object(Tab, Pattern, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="member-2"></a>
|
||||||
|
|
||||||
|
###member/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`member(Tab, Key) -> any()`
|
||||||
|
|
||||||
|
<a name="next-2"></a>
|
||||||
|
|
||||||
|
###next/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`next(Tab, Key) -> any()`
|
||||||
|
|
||||||
<a name="open-1"></a>
|
<a name="open-1"></a>
|
||||||
|
|
||||||
|
@ -127,6 +235,15 @@
|
||||||
|
|
||||||
`open(Tab) -> any()`
|
`open(Tab) -> any()`
|
||||||
|
|
||||||
|
<a name="prev-2"></a>
|
||||||
|
|
||||||
|
###prev/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`prev(Tab, Key) -> any()`
|
||||||
|
|
||||||
<a name="repair-1"></a>
|
<a name="repair-1"></a>
|
||||||
|
|
||||||
###repair/1##
|
###repair/1##
|
||||||
|
@ -136,12 +253,84 @@
|
||||||
|
|
||||||
`repair(Tab) -> any()`
|
`repair(Tab) -> any()`
|
||||||
|
|
||||||
<a name="tab2list-2"></a>
|
<a name="select-1"></a>
|
||||||
|
|
||||||
###tab2list/2##
|
###select/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`tab2list(Tab, Ets) -> any()`
|
`select(Cont) -> any()`
|
||||||
|
|
||||||
|
<a name="select-2"></a>
|
||||||
|
|
||||||
|
###select/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select-3"></a>
|
||||||
|
|
||||||
|
###select/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select(Tab, Spec, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="select_count-2"></a>
|
||||||
|
|
||||||
|
###select_count/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_count(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select_delete-2"></a>
|
||||||
|
|
||||||
|
###select_delete/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_delete(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select_reverse-1"></a>
|
||||||
|
|
||||||
|
###select_reverse/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_reverse(Cont) -> any()`
|
||||||
|
|
||||||
|
<a name="select_reverse-2"></a>
|
||||||
|
|
||||||
|
###select_reverse/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_reverse(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select_reverse-3"></a>
|
||||||
|
|
||||||
|
###select_reverse/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_reverse(Tab, Spec, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="tab2list-1"></a>
|
||||||
|
|
||||||
|
###tab2list/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`tab2list(Tab) -> any()`
|
||||||
|
|
||||||
|
|
265
doc/lets_nif.md
265
doc/lets_nif.md
|
@ -12,13 +12,22 @@
|
||||||
##Function Index##
|
##Function Index##
|
||||||
|
|
||||||
|
|
||||||
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#delete-2">delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#delete-3">delete/3</a></td><td></td></tr><tr><td valign="top"><a href="#delete_all_objects-2">delete_all_objects/2</a></td><td></td></tr><tr><td valign="top"><a href="#destroy-4">destroy/4</a></td><td></td></tr><tr><td valign="top"><a href="#first-2">first/2</a></td><td></td></tr><tr><td valign="top"><a href="#info_memory-2">info_memory/2</a></td><td></td></tr><tr><td valign="top"><a href="#info_size-2">info_size/2</a></td><td></td></tr><tr><td valign="top"><a href="#insert-3">insert/3</a></td><td></td></tr><tr><td valign="top"><a href="#insert_new-3">insert_new/3</a></td><td></td></tr><tr><td valign="top"><a href="#lookup-3">lookup/3</a></td><td></td></tr><tr><td valign="top"><a href="#next-3">next/3</a></td><td></td></tr><tr><td valign="top"><a href="#open-4">open/4</a></td><td></td></tr><tr><td valign="top"><a href="#repair-4">repair/4</a></td><td></td></tr><tr><td valign="top"><a href="#tab2list-2">tab2list/2</a></td><td></td></tr></table>
|
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#delete-1">delete/1</a></td><td></td></tr><tr><td valign="top"><a href="#delete-2">delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#delete_all_objects-1">delete_all_objects/1</a></td><td></td></tr><tr><td valign="top"><a href="#destroy-4">destroy/4</a></td><td></td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td></td></tr><tr><td valign="top"><a href="#foldl-3">foldl/3</a></td><td></td></tr><tr><td valign="top"><a href="#foldr-3">foldr/3</a></td><td></td></tr><tr><td valign="top"><a href="#info_memory-1">info_memory/1</a></td><td></td></tr><tr><td valign="top"><a href="#info_size-1">info_size/1</a></td><td></td></tr><tr><td valign="top"><a href="#insert-2">insert/2</a></td><td></td></tr><tr><td valign="top"><a href="#insert_new-2">insert_new/2</a></td><td></td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td></td></tr><tr><td valign="top"><a href="#lookup-2">lookup/2</a></td><td></td></tr><tr><td valign="top"><a href="#lookup_element-3">lookup_element/3</a></td><td></td></tr><tr><td valign="top"><a href="#match-1">match/1</a></td><td></td></tr><tr><td valign="top"><a href="#match-2">match/2</a></td><td></td></tr><tr><td valign="top"><a href="#match-3">match/3</a></td><td></td></tr><tr><td valign="top"><a href="#match_delete-2">match_delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#match_object-1">match_object/1</a></td><td></td></tr><tr><td valign="top"><a href="#match_object-2">match_object/2</a></td><td></td></tr><tr><td valign="top"><a href="#match_object-3">match_object/3</a></td><td></td></tr><tr><td valign="top"><a href="#member-2">member/2</a></td><td></td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td></td></tr><tr><td valign="top"><a href="#open-4">open/4</a></td><td></td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td></td></tr><tr><td valign="top"><a href="#repair-4">repair/4</a></td><td></td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td></td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td></td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td></td></tr><tr><td valign="top"><a href="#select_count-2">select_count/2</a></td><td></td></tr><tr><td valign="top"><a href="#select_delete-2">select_delete/2</a></td><td></td></tr><tr><td valign="top"><a href="#select_reverse-1">select_reverse/1</a></td><td></td></tr><tr><td valign="top"><a href="#select_reverse-2">select_reverse/2</a></td><td></td></tr><tr><td valign="top"><a href="#select_reverse-3">select_reverse/3</a></td><td></td></tr><tr><td valign="top"><a href="#tab2list-1">tab2list/1</a></td><td></td></tr></table>
|
||||||
|
|
||||||
|
|
||||||
<a name="functions"></a>
|
<a name="functions"></a>
|
||||||
|
|
||||||
##Function Details##
|
##Function Details##
|
||||||
|
|
||||||
|
<a name="delete-1"></a>
|
||||||
|
|
||||||
|
###delete/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`delete(Tab) -> any()`
|
||||||
|
|
||||||
<a name="delete-2"></a>
|
<a name="delete-2"></a>
|
||||||
|
|
||||||
###delete/2##
|
###delete/2##
|
||||||
|
@ -26,25 +35,16 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`delete(Tab, Impl) -> any()`
|
`delete(Tab, Key) -> any()`
|
||||||
|
|
||||||
<a name="delete-3"></a>
|
<a name="delete_all_objects-1"></a>
|
||||||
|
|
||||||
###delete/3##
|
###delete_all_objects/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`delete(Tab, Impl, Key) -> any()`
|
`delete_all_objects(Tab) -> any()`
|
||||||
|
|
||||||
<a name="delete_all_objects-2"></a>
|
|
||||||
|
|
||||||
###delete_all_objects/2##
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`delete_all_objects(Tab, Impl) -> any()`
|
|
||||||
|
|
||||||
<a name="destroy-4"></a>
|
<a name="destroy-4"></a>
|
||||||
|
|
||||||
|
@ -55,68 +55,176 @@
|
||||||
|
|
||||||
`destroy(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
`destroy(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
||||||
|
|
||||||
<a name="first-2"></a>
|
<a name="first-1"></a>
|
||||||
|
|
||||||
###first/2##
|
###first/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`first(Tab, Impl) -> any()`
|
`first(Tab) -> any()`
|
||||||
|
|
||||||
<a name="info_memory-2"></a>
|
<a name="foldl-3"></a>
|
||||||
|
|
||||||
###info_memory/2##
|
###foldl/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`info_memory(Tab, Impl) -> any()`
|
`foldl(Fun, Acc0, Tab) -> any()`
|
||||||
|
|
||||||
<a name="info_size-2"></a>
|
<a name="foldr-3"></a>
|
||||||
|
|
||||||
###info_size/2##
|
###foldr/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`info_size(Tab, Impl) -> any()`
|
`foldr(Fun, Acc0, Tab) -> any()`
|
||||||
|
|
||||||
<a name="insert-3"></a>
|
<a name="info_memory-1"></a>
|
||||||
|
|
||||||
###insert/3##
|
###info_memory/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`insert(Tab, Impl, Object) -> any()`
|
`info_memory(Tab) -> any()`
|
||||||
|
|
||||||
<a name="insert_new-3"></a>
|
<a name="info_size-1"></a>
|
||||||
|
|
||||||
###insert_new/3##
|
###info_size/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`insert_new(Tab, Impl, Object) -> any()`
|
`info_size(Tab) -> any()`
|
||||||
|
|
||||||
<a name="lookup-3"></a>
|
<a name="insert-2"></a>
|
||||||
|
|
||||||
###lookup/3##
|
###insert/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`lookup(Tab, Impl, Key) -> any()`
|
`insert(Tab, Object) -> any()`
|
||||||
|
|
||||||
<a name="next-3"></a>
|
<a name="insert_new-2"></a>
|
||||||
|
|
||||||
###next/3##
|
###insert_new/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`next(Tab, Impl, Key) -> any()`
|
`insert_new(Tab, Object) -> any()`
|
||||||
|
|
||||||
|
<a name="last-1"></a>
|
||||||
|
|
||||||
|
###last/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`last(Tab) -> any()`
|
||||||
|
|
||||||
|
<a name="lookup-2"></a>
|
||||||
|
|
||||||
|
###lookup/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`lookup(Tab, Key) -> any()`
|
||||||
|
|
||||||
|
<a name="lookup_element-3"></a>
|
||||||
|
|
||||||
|
###lookup_element/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`lookup_element(Tab, Key, Pos) -> any()`
|
||||||
|
|
||||||
|
<a name="match-1"></a>
|
||||||
|
|
||||||
|
###match/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match(Cont) -> any()`
|
||||||
|
|
||||||
|
<a name="match-2"></a>
|
||||||
|
|
||||||
|
###match/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match(Tab, Pattern) -> any()`
|
||||||
|
|
||||||
|
<a name="match-3"></a>
|
||||||
|
|
||||||
|
###match/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match(Tab, Pattern, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="match_delete-2"></a>
|
||||||
|
|
||||||
|
###match_delete/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_delete(Tab, Pattern) -> any()`
|
||||||
|
|
||||||
|
<a name="match_object-1"></a>
|
||||||
|
|
||||||
|
###match_object/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_object(Cont) -> any()`
|
||||||
|
|
||||||
|
<a name="match_object-2"></a>
|
||||||
|
|
||||||
|
###match_object/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_object(Tab, Pattern) -> any()`
|
||||||
|
|
||||||
|
<a name="match_object-3"></a>
|
||||||
|
|
||||||
|
###match_object/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`match_object(Tab, Pattern, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="member-2"></a>
|
||||||
|
|
||||||
|
###member/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`member(Tab, Key) -> any()`
|
||||||
|
|
||||||
|
<a name="next-2"></a>
|
||||||
|
|
||||||
|
###next/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`next(Tab, Key) -> any()`
|
||||||
|
|
||||||
<a name="open-4"></a>
|
<a name="open-4"></a>
|
||||||
|
|
||||||
|
@ -127,6 +235,15 @@
|
||||||
|
|
||||||
`open(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
`open(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
||||||
|
|
||||||
|
<a name="prev-2"></a>
|
||||||
|
|
||||||
|
###prev/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`prev(Tab, Key) -> any()`
|
||||||
|
|
||||||
<a name="repair-4"></a>
|
<a name="repair-4"></a>
|
||||||
|
|
||||||
###repair/4##
|
###repair/4##
|
||||||
|
@ -136,12 +253,84 @@
|
||||||
|
|
||||||
`repair(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
`repair(Tab, Options, ReadOptions, WriteOptions) -> any()`
|
||||||
|
|
||||||
<a name="tab2list-2"></a>
|
<a name="select-1"></a>
|
||||||
|
|
||||||
###tab2list/2##
|
###select/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`tab2list(Tab, Impl) -> any()`
|
`select(Cont0) -> any()`
|
||||||
|
|
||||||
|
<a name="select-2"></a>
|
||||||
|
|
||||||
|
###select/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select-3"></a>
|
||||||
|
|
||||||
|
###select/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select(Tab, Spec, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="select_count-2"></a>
|
||||||
|
|
||||||
|
###select_count/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_count(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select_delete-2"></a>
|
||||||
|
|
||||||
|
###select_delete/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_delete(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select_reverse-1"></a>
|
||||||
|
|
||||||
|
###select_reverse/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_reverse(Cont0) -> any()`
|
||||||
|
|
||||||
|
<a name="select_reverse-2"></a>
|
||||||
|
|
||||||
|
###select_reverse/2##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_reverse(Tab, Spec) -> any()`
|
||||||
|
|
||||||
|
<a name="select_reverse-3"></a>
|
||||||
|
|
||||||
|
###select_reverse/3##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`select_reverse(Tab, Spec, Limit) -> any()`
|
||||||
|
|
||||||
|
<a name="tab2list-1"></a>
|
||||||
|
|
||||||
|
###tab2list/1##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`tab2list(Tab) -> any()`
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,8 @@ LETS is an alternative Erlang Term Storage using LevelDB as the
|
||||||
storage implementation. LETS tries to address some bad properties of
|
storage implementation. LETS tries to address some bad properties of
|
||||||
ETS and DETS. ETS is limited by physical memory. DETS is limited by
|
ETS and DETS. ETS is limited by physical memory. DETS is limited by
|
||||||
a 2 GB file size limitation and does not implement ordered sets. LETS
|
a 2 GB file size limitation and does not implement ordered sets. LETS
|
||||||
has neither of these limitations.
|
has neither of these limitations. Data can also be automatically
|
||||||
|
compressed using the Snappy compression library.
|
||||||
|
|
||||||
For testing and comparison purposes, LETS supports three
|
For testing and comparison purposes, LETS supports three
|
||||||
implementations:
|
implementations:
|
||||||
|
@ -18,20 +19,40 @@ implementations:
|
||||||
- +ets+ Erlang ETS backend
|
- +ets+ Erlang ETS backend
|
||||||
|
|
||||||
LETS is not intended to be an exact clone of ETS. The currently
|
LETS is not intended to be an exact clone of ETS. The currently
|
||||||
supported APIs are:
|
supported ETS APIs are:
|
||||||
|
|
||||||
- +new/2+
|
|
||||||
- +destroy/2+ _only driver and nif implementations_
|
|
||||||
- +repair/2+ _only driver and nif implementations_
|
|
||||||
- +insert/2+
|
|
||||||
- +insert_new/2+ _only the ets implementation_
|
|
||||||
- +delete/1+
|
- +delete/1+
|
||||||
- +delete/2+
|
- +delete/2+
|
||||||
- +delete_all_objects/1+ _only the ets implementation_
|
- +delete_all_objects/1+ _only ets implementation_
|
||||||
- +lookup/2+
|
|
||||||
- +first/1+
|
- +first/1+
|
||||||
- +next/2+
|
- +foldl/3+
|
||||||
|
- +foldr/3+
|
||||||
|
- +info/1+ _only a subset of items_
|
||||||
- +info/2+ _only a subset of items_
|
- +info/2+ _only a subset of items_
|
||||||
|
- +insert/2+
|
||||||
|
- +insert_new/2+ _only ets implementation_
|
||||||
|
- +last/1+
|
||||||
|
- +lookup/2+
|
||||||
|
- +lookup_element/3+
|
||||||
|
- +match/1+
|
||||||
|
- +match/2+
|
||||||
|
- +match/3+
|
||||||
|
- +match_delete/2+
|
||||||
|
- +match_object/1+
|
||||||
|
- +match_object/2+
|
||||||
|
- +match_object/3+
|
||||||
|
- +member/2+
|
||||||
|
- +new/2+
|
||||||
|
- +next/2+
|
||||||
|
- +prev/2+
|
||||||
|
- +select/1+
|
||||||
|
- +select/2+
|
||||||
|
- +select/3+
|
||||||
|
- +select_count/2+
|
||||||
|
- +select_delete/2+
|
||||||
|
- +select_reverse/1+
|
||||||
|
- +select_reverse/2+
|
||||||
|
- +select_reverse/3+
|
||||||
- +tab2list/1+
|
- +tab2list/1+
|
||||||
|
|
||||||
_This repository is experimental in nature - use at your own risk and
|
_This repository is experimental in nature - use at your own risk and
|
||||||
|
@ -63,7 +84,7 @@ please read further.
|
||||||
|
|
||||||
This README is the only bit of documentation right now.
|
This README is the only bit of documentation right now.
|
||||||
|
|
||||||
The QC (a.k.a. QuickCheck, Proper, etc.) tests underneath the
|
The QC (a.k.a. QuickCheck, PropEr, etc.) tests underneath the
|
||||||
"tests/qc" directory should be helpful for understanding the
|
"tests/qc" directory should be helpful for understanding the
|
||||||
specification and behavior of ETS and LETS. These QC tests also
|
specification and behavior of ETS and LETS. These QC tests also
|
||||||
illustrate several strategies for testing Erlang Driver-based and
|
illustrate several strategies for testing Erlang Driver-based and
|
||||||
|
@ -85,6 +106,12 @@ provides an ordered mapping from string keys to string values.
|
||||||
|
|
||||||
See http://code.google.com/p/leveldb/ for further details.
|
See http://code.google.com/p/leveldb/ for further details.
|
||||||
|
|
||||||
|
=== What is Snappy?
|
||||||
|
|
||||||
|
Snappy is a fast compression/decompression library written at Google.
|
||||||
|
|
||||||
|
See http://code.google.com/p/snappy/ for further details.
|
||||||
|
|
||||||
== To download
|
== To download
|
||||||
|
|
||||||
1. Configure your e-mail and name for Git
|
1. Configure your e-mail and name for Git
|
||||||
|
@ -212,16 +239,45 @@ $ make compile-eqc eqc-compile
|
||||||
$ cd working-directory-name/src/lib/lets/.eunit
|
$ cd working-directory-name/src/lib/lets/.eunit
|
||||||
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
||||||
|
|
||||||
1> qc_statem_lets:run(5000).
|
1> qc_statem_lets:qc_run(5000).
|
||||||
|
....
|
||||||
|
OK, passed 5000 tests
|
||||||
|
|
||||||
|
9.022% {delete,ok}
|
||||||
|
7.800% {new,ok}
|
||||||
|
4.535% {match_delete,ok}
|
||||||
|
4.491% {lookup,ok}
|
||||||
|
4.399% {select,ok}
|
||||||
|
4.352% {select_delete,ok}
|
||||||
|
4.348% {tab2list,ok}
|
||||||
|
4.341% {member,ok}
|
||||||
|
4.334% {last,ok}
|
||||||
|
4.315% {foldl,ok}
|
||||||
|
4.308% {select_reverse,ok}
|
||||||
|
4.301% {select_count,ok}
|
||||||
|
4.293% {select31,ok}
|
||||||
|
4.264% {first,ok}
|
||||||
|
4.216% {foldr,ok}
|
||||||
|
4.202% {match_object,ok}
|
||||||
|
4.184% {match,ok}
|
||||||
|
4.056% {insert,ok}
|
||||||
|
3.997% {prev,ok}
|
||||||
|
3.774% {next,ok}
|
||||||
|
3.416% {lookup_element,{error,badarg}}
|
||||||
|
1.298% {insert_new,ok}
|
||||||
|
0.757% {lookup_element,ok}
|
||||||
|
0.516% {next,{error,badarg}}
|
||||||
|
0.483% {prev,{error,badarg}}
|
||||||
|
true
|
||||||
.......
|
.......
|
||||||
------
|
------
|
||||||
+
|
+
|
||||||
TIP: For testing LevelDB directly using the C bindings, try
|
TIP: For testing LevelDB directly using the C bindings, try
|
||||||
+qc_statemc_lets:run(5000)+.
|
+qc_statemc_lets:qc_run(5000)+.
|
||||||
|
|
||||||
== To test - Proper
|
== To test - PropEr
|
||||||
|
|
||||||
1. Make sure Proper is in your Erlang code path. One simple way to
|
1. Make sure PropEr is in your Erlang code path. One simple way to
|
||||||
accomplish this is by adding the code path to your +~/.erlang+
|
accomplish this is by adding the code path to your +~/.erlang+
|
||||||
resource file.
|
resource file.
|
||||||
+
|
+
|
||||||
|
@ -229,7 +285,7 @@ TIP: For testing LevelDB directly using the C bindings, try
|
||||||
true = code:add_pathz(os:getenv("HOME")++"/.erlang.d/lib/proper/ebin").
|
true = code:add_pathz(os:getenv("HOME")++"/.erlang.d/lib/proper/ebin").
|
||||||
------
|
------
|
||||||
|
|
||||||
2. Compile for Proper
|
2. Compile for PropEr
|
||||||
+
|
+
|
||||||
------
|
------
|
||||||
$ cd working-directory-name/src
|
$ cd working-directory-name/src
|
||||||
|
@ -237,20 +293,49 @@ $ make clean
|
||||||
$ make compile-proper proper-compile
|
$ make compile-proper proper-compile
|
||||||
------
|
------
|
||||||
|
|
||||||
3. Run 5,000 Proper tests
|
3. Run 5,000 PropEr tests
|
||||||
+
|
+
|
||||||
------
|
------
|
||||||
$ cd working-directory-name/src/lib/lets/.eunit
|
$ cd working-directory-name/src/lib/lets/.eunit
|
||||||
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
$ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
||||||
|
|
||||||
1> qc_statem_lets:run(5000).
|
1> qc_statem_lets:qc_run(5000).
|
||||||
|
....
|
||||||
|
OK: Passed 5000 test(s).
|
||||||
|
|
||||||
|
11% {new,ok}
|
||||||
|
8% {delete,ok}
|
||||||
|
4% {member,ok}
|
||||||
|
4% {select,ok}
|
||||||
|
4% {select_count,ok}
|
||||||
|
4% {select_reverse,ok}
|
||||||
|
4% {lookup,ok}
|
||||||
|
4% {match_object,ok}
|
||||||
|
4% {tab2list,ok}
|
||||||
|
4% {last,ok}
|
||||||
|
4% {match,ok}
|
||||||
|
4% {foldl,ok}
|
||||||
|
4% {match_delete,ok}
|
||||||
|
3% {prev,ok}
|
||||||
|
3% {select31,ok}
|
||||||
|
3% {select_delete,ok}
|
||||||
|
3% {foldr,ok}
|
||||||
|
3% {insert,ok}
|
||||||
|
3% {first,ok}
|
||||||
|
3% {next,ok}
|
||||||
|
3% {lookup_element,{error,badarg}}
|
||||||
|
1% {insert_new,ok}
|
||||||
|
0% {prev,{error,badarg}}
|
||||||
|
0% {lookup_element,ok}
|
||||||
|
0% {next,{error,badarg}}
|
||||||
|
true
|
||||||
.......
|
.......
|
||||||
------
|
------
|
||||||
|
|
||||||
== Roadmap
|
== Roadmap
|
||||||
|
|
||||||
- Documentation
|
- Documentation
|
||||||
* Explain how to run QuickCheck/Proper tests using a new rebar
|
* Explain how to run QuickCheck/PropEr tests using a new rebar
|
||||||
plugin.
|
plugin.
|
||||||
* Explain how to build and to run lets with valgrind enabled
|
* Explain how to build and to run lets with valgrind enabled
|
||||||
OTP/Erlang virtual machine
|
OTP/Erlang virtual machine
|
||||||
|
@ -271,6 +356,10 @@ $ erl -smp +A 5 -pz ../../sext/ebin -pz ../../qc/ebin
|
||||||
* +delete_all_objects/1+
|
* +delete_all_objects/1+
|
||||||
(http://code.google.com/p/leveldb/issues/detail?id=43)
|
(http://code.google.com/p/leveldb/issues/detail?id=43)
|
||||||
|
|
||||||
|
* Add custom (i.e. not supported by native ETS) APIs for providing
|
||||||
|
access to LevelDB\'s iterators for +drv+ and +nif+ backend
|
||||||
|
implementations.
|
||||||
|
|
||||||
- Existing APIs (TBD)
|
- Existing APIs (TBD)
|
||||||
* +delete/1+
|
* +delete/1+
|
||||||
(http://code.google.com/p/leveldb/issues/detail?id=48)
|
(http://code.google.com/p/leveldb/issues/detail?id=48)
|
||||||
|
|
596
src/lets.erl
596
src/lets.erl
|
@ -28,20 +28,108 @@
|
||||||
-export([new/2
|
-export([new/2
|
||||||
, destroy/2
|
, destroy/2
|
||||||
, repair/2
|
, repair/2
|
||||||
, insert/2
|
|
||||||
, insert_new/2
|
|
||||||
, delete/1
|
, delete/1
|
||||||
, delete/2
|
, delete/2
|
||||||
, delete_all_objects/1
|
, delete_all_objects/1
|
||||||
, lookup/2
|
|
||||||
, first/1
|
, first/1
|
||||||
, next/2
|
, foldl/3
|
||||||
|
, foldr/3
|
||||||
|
, info/1
|
||||||
, info/2
|
, info/2
|
||||||
|
, insert/2
|
||||||
|
, insert_new/2
|
||||||
|
, last/1
|
||||||
|
, lookup/2
|
||||||
|
, lookup_element/3
|
||||||
|
, match/2
|
||||||
|
, match/3
|
||||||
|
, match/1
|
||||||
|
, match_delete/2
|
||||||
|
, match_object/2
|
||||||
|
, match_object/3
|
||||||
|
, match_object/1
|
||||||
|
, member/2
|
||||||
|
, next/2
|
||||||
|
, prev/2
|
||||||
|
, select/2
|
||||||
|
, select/3
|
||||||
|
, select/1
|
||||||
|
, select_count/2
|
||||||
|
, select_delete/2
|
||||||
|
, select_reverse/2
|
||||||
|
, select_reverse/3
|
||||||
|
, select_reverse/1
|
||||||
, tab2list/1
|
, tab2list/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export_type([tab/0]).
|
-export_type([tab/0]).
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% ETS exports
|
||||||
|
%%
|
||||||
|
%% -export([all/0
|
||||||
|
%% , delete/1 %% mnesia
|
||||||
|
%% , delete/2 %% mnesia
|
||||||
|
%% , delete_all_objects/1
|
||||||
|
%% , delete_object/2
|
||||||
|
%% , file2tab/1
|
||||||
|
%% , file2tab/2
|
||||||
|
%% , filter/3 %% mnesia
|
||||||
|
%% , first/1 %% mnesia
|
||||||
|
%% , foldl/3 %% mnesia
|
||||||
|
%% , foldr/3
|
||||||
|
%% , from_dets/2
|
||||||
|
%% , fun2ms/1
|
||||||
|
%% , give_away/3
|
||||||
|
%% , i/0
|
||||||
|
%% , i/1
|
||||||
|
%% , info/1
|
||||||
|
%% , info/2 %% mnesia
|
||||||
|
%% , init_table/2 %% mnesia
|
||||||
|
%% , insert/2 %% mnesia
|
||||||
|
%% , insert_new/2
|
||||||
|
%% , is_compiled_ms/1
|
||||||
|
%% , last/1 %% mnesia
|
||||||
|
%% , lookup/2 %% mnesia
|
||||||
|
%% , lookup_element/3 %% mnesia
|
||||||
|
%% , match/1
|
||||||
|
%% , match/2 %% mnesia
|
||||||
|
%% , match/3
|
||||||
|
%% , match_delete/2 %% mnesia
|
||||||
|
%% , match_object/1
|
||||||
|
%% , match_object/2 %% mnesia
|
||||||
|
%% , match_object/3
|
||||||
|
%% , match_spec_compile/1
|
||||||
|
%% , match_spec_run/2 %% mnesia
|
||||||
|
%% , member/2
|
||||||
|
%% , new/2 %% mnesia
|
||||||
|
%% , next/2 %% mnesia
|
||||||
|
%% , prev/2 %% mnesia
|
||||||
|
%% , rename/2
|
||||||
|
%% , repair_continuation/2 %% mnesia
|
||||||
|
%% , safe_fixtable/2
|
||||||
|
%% , select/1
|
||||||
|
%% , select/2
|
||||||
|
%% , select/3
|
||||||
|
%% , select_count/2
|
||||||
|
%% , select_delete/2
|
||||||
|
%% , select_reverse/1
|
||||||
|
%% , select_reverse/2
|
||||||
|
%% , select_reverse/3
|
||||||
|
%% , setopts/2
|
||||||
|
%% , slot/2 %% mnesia
|
||||||
|
%% , tab2file/2
|
||||||
|
%% , tab2file/3
|
||||||
|
%% , tab2list/1 %% mnesia
|
||||||
|
%% , tabfile_info/1
|
||||||
|
%% , table/1
|
||||||
|
%% , table/2
|
||||||
|
%% , test_ms/2
|
||||||
|
%% , to_dets/2
|
||||||
|
%% , update_counter/3 %% mnesia
|
||||||
|
%% , update_element/3
|
||||||
|
%% ]).
|
||||||
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Types/Specs/Records
|
%%% Types/Specs/Records
|
||||||
|
@ -60,6 +148,14 @@
|
||||||
-type key() :: binary().
|
-type key() :: binary().
|
||||||
-type object() :: term().
|
-type object() :: term().
|
||||||
|
|
||||||
|
-type name() :: atom().
|
||||||
|
-type item() :: owner | name | named_table | type | keypos | protection | compressed | async | memory | size.
|
||||||
|
-type pos() :: pos_integer().
|
||||||
|
-type pattern() :: atom() | tuple(). %% ets:match_pattern() is not exported!
|
||||||
|
-type spec() :: ets:match_spec().
|
||||||
|
-type match() :: term().
|
||||||
|
-type limit() :: pos_integer().
|
||||||
|
-opaque cont() :: {cont, tab(), term()}.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% API
|
%%% API
|
||||||
|
@ -164,7 +260,7 @@
|
||||||
%% @end
|
%% @end
|
||||||
%% @see ets:new/2
|
%% @see ets:new/2
|
||||||
|
|
||||||
-spec new(Name::atom(), Options::opts()) -> tab().
|
-spec new(name(), opts()) -> tab().
|
||||||
new(Name, Opts) ->
|
new(Name, Opts) ->
|
||||||
create(open, Name, Opts).
|
create(open, Name, Opts).
|
||||||
|
|
||||||
|
@ -172,11 +268,10 @@ new(Name, Opts) ->
|
||||||
%% only applies to +driver+ and +nif+ implementations.
|
%% only applies to +driver+ and +nif+ implementations.
|
||||||
%% @end
|
%% @end
|
||||||
|
|
||||||
-spec destroy(Name::atom(), Options::opts()) -> true.
|
-spec destroy(name(), opts()) -> true.
|
||||||
destroy(Name, Opts) ->
|
destroy(Name, Opts) ->
|
||||||
create(destroy, Name, Opts).
|
create(destroy, Name, Opts).
|
||||||
|
|
||||||
|
|
||||||
%% @doc If a table cannot be opened, you may attempt to call this
|
%% @doc If a table cannot be opened, you may attempt to call this
|
||||||
%% method to resurrect as much of the contents of the table as
|
%% method to resurrect as much of the contents of the table as
|
||||||
%% possible. Some data may be lost, so be careful when calling this
|
%% possible. Some data may be lost, so be careful when calling this
|
||||||
|
@ -184,40 +279,10 @@ destroy(Name, Opts) ->
|
||||||
%% function only applies to +driver+ and +nif+ implementations.
|
%% function only applies to +driver+ and +nif+ implementations.
|
||||||
%% @end
|
%% @end
|
||||||
|
|
||||||
-spec repair(Name::atom(), Options::opts()) -> true.
|
-spec repair(name(), opts()) -> true.
|
||||||
repair(Name, Opts) ->
|
repair(Name, Opts) ->
|
||||||
create(repair, Name, Opts).
|
create(repair, Name, Opts).
|
||||||
|
|
||||||
%% @doc Inserts the object or all of the objects in the list
|
|
||||||
%% +ObjectOrObjects+ into the table +Tab+.
|
|
||||||
%% @end
|
|
||||||
%% @see ets:insert/2
|
|
||||||
|
|
||||||
-spec insert(Tab::tab(), ObjectOrObjects::object() | [object()]) -> true.
|
|
||||||
insert(Tab, ObjectOrObjects) ->
|
|
||||||
case check_access(Tab) of
|
|
||||||
undefined ->
|
|
||||||
erlang:error(badarg, [Tab]);
|
|
||||||
{Mod, Impl} ->
|
|
||||||
Mod:insert(Tab, Impl, ObjectOrObjects)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @doc This function works exactly like +insert/2+, with the
|
|
||||||
%% exception that instead of overwriting objects with the same key, it
|
|
||||||
%% simply returns false. This function only applies to the +ets+
|
|
||||||
%% implementation.
|
|
||||||
%% @end
|
|
||||||
%% @see ets:insert_new/2
|
|
||||||
|
|
||||||
-spec insert_new(tab(), object() | [object()]) -> true.
|
|
||||||
insert_new(Tab, ObjectOrObjects) ->
|
|
||||||
case check_access(Tab) of
|
|
||||||
undefined ->
|
|
||||||
erlang:error(badarg, [Tab]);
|
|
||||||
{Mod, Impl} ->
|
|
||||||
Mod:insert_new(Tab, Impl, ObjectOrObjects)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @doc Deletes the entire table +Tab+.
|
%% @doc Deletes the entire table +Tab+.
|
||||||
%% @end
|
%% @end
|
||||||
%% @see ets:delete/1
|
%% @see ets:delete/1
|
||||||
|
@ -227,8 +292,8 @@ delete(Tab) ->
|
||||||
case check_access(Tab) of
|
case check_access(Tab) of
|
||||||
undefined ->
|
undefined ->
|
||||||
erlang:error(badarg, [Tab]);
|
erlang:error(badarg, [Tab]);
|
||||||
{Mod, Impl} ->
|
Mod ->
|
||||||
Mod:delete(Tab, Impl)
|
Mod:delete(Tab)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Deletes all objects with the key +Key+ from the table +Tab+.
|
%% @doc Deletes all objects with the key +Key+ from the table +Tab+.
|
||||||
|
@ -240,8 +305,8 @@ delete(Tab, Key) ->
|
||||||
case check_access(Tab) of
|
case check_access(Tab) of
|
||||||
undefined ->
|
undefined ->
|
||||||
erlang:error(badarg, [Tab]);
|
erlang:error(badarg, [Tab]);
|
||||||
{Mod, Impl} ->
|
Mod ->
|
||||||
Mod:delete(Tab, Impl, Key)
|
Mod:delete(Tab, Key)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Delete all objects in the table +Tab+. The operation is
|
%% @doc Delete all objects in the table +Tab+. The operation is
|
||||||
|
@ -255,22 +320,8 @@ delete_all_objects(Tab) ->
|
||||||
case check_access(Tab) of
|
case check_access(Tab) of
|
||||||
undefined ->
|
undefined ->
|
||||||
erlang:error(badarg, [Tab]);
|
erlang:error(badarg, [Tab]);
|
||||||
{Mod, Impl} ->
|
Mod ->
|
||||||
Mod:delete_all_objects(Tab, Impl)
|
Mod:delete_all_objects(Tab)
|
||||||
end.
|
|
||||||
|
|
||||||
%% @doc Returns a list of all objects with the key +Key+ in the table
|
|
||||||
%% +Tab+.
|
|
||||||
%% @end
|
|
||||||
%% @see ets:lookup/2
|
|
||||||
|
|
||||||
-spec lookup(tab(), key()) -> [object()].
|
|
||||||
lookup(Tab, Key) ->
|
|
||||||
case check_access(Tab) of
|
|
||||||
undefined ->
|
|
||||||
erlang:error(badarg, [Tab]);
|
|
||||||
{Mod, Impl} ->
|
|
||||||
Mod:lookup(Tab, Impl, Key)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Returns the first key +Key+ in the table +Tab+. If the table
|
%% @doc Returns the first key +Key+ in the table +Tab+. If the table
|
||||||
|
@ -283,28 +334,64 @@ first(Tab) ->
|
||||||
case check_access(Tab) of
|
case check_access(Tab) of
|
||||||
undefined ->
|
undefined ->
|
||||||
erlang:error(badarg, [Tab]);
|
erlang:error(badarg, [Tab]);
|
||||||
{Mod, Impl} ->
|
Mod ->
|
||||||
Mod:first(Tab, Impl)
|
Mod:first(Tab)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Returns the next key +Key2+, following the key +Key1+ in the
|
%% @doc Fold from left to right over the elements of the table.
|
||||||
%% table +Tab+. If there is no next key, +'$end_of_table'+ is
|
|
||||||
%% returned.
|
|
||||||
%% @end
|
%% @end
|
||||||
%% @see ets:next/2
|
%% @see ets:foldl/3
|
||||||
|
|
||||||
-spec next(tab(), key()) -> key() | '$end_of_table'.
|
-spec foldl(Fun, Acc0::term(), tab()) -> Acc1::term() when
|
||||||
next(Tab, Key) ->
|
Fun :: fun((Element::term(), AccIn::term()) -> AccOut::term()).
|
||||||
|
foldl(Function, Acc0, Tab) ->
|
||||||
case check_access(Tab) of
|
case check_access(Tab) of
|
||||||
undefined ->
|
undefined ->
|
||||||
erlang:error(badarg, [Tab]);
|
erlang:error(badarg, [Tab]);
|
||||||
{Mod, Impl} ->
|
Mod ->
|
||||||
Mod:next(Tab, Impl, Key)
|
Mod:foldl(Function, Acc0, Tab)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Fold from right to left over the elements of the table.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:foldr/3
|
||||||
|
|
||||||
|
-spec foldr(Fun, Acc0::term(), tab()) -> Acc1::term() when
|
||||||
|
Fun :: fun((Element::term(), AccIn::term()) -> AccOut::term()).
|
||||||
|
foldr(Function, Acc0, Tab) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:foldr(Function, Acc0, Tab)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Returns information about the table +Tab+ as a list of +{Item,
|
%% @doc Returns information about the table +Tab+ as a list of +{Item,
|
||||||
%% Value}+ tuples.
|
%% Value}+ tuples.
|
||||||
%%
|
%%
|
||||||
|
%% @end
|
||||||
|
%% @see info/2
|
||||||
|
|
||||||
|
-spec info(tab()) -> [{item(), term()}].
|
||||||
|
info(Tab) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
[{owner, Tab#tab.owner},
|
||||||
|
{name, Tab#tab.name},
|
||||||
|
{named_table, Tab#tab.named_table},
|
||||||
|
{type, Tab#tab.type},
|
||||||
|
{keypos, Tab#tab.keypos},
|
||||||
|
{protection, Tab#tab.protection},
|
||||||
|
{compressed, Tab#tab.compressed},
|
||||||
|
{async, Tab#tab.async},
|
||||||
|
{memory, Mod:info_memory(Tab)},
|
||||||
|
{size, Mod:info_size(Tab)}]
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Returns the information associated with +Item+ for the table +Tab+.
|
||||||
|
%%
|
||||||
%% Valid +Item+ options are:
|
%% Valid +Item+ options are:
|
||||||
%%
|
%%
|
||||||
%% - +owner+
|
%% - +owner+
|
||||||
|
@ -321,12 +408,12 @@ next(Tab, Key) ->
|
||||||
%% @end
|
%% @end
|
||||||
%% @see ets:info/2
|
%% @see ets:info/2
|
||||||
|
|
||||||
-spec info(tab(), atom()) -> term().
|
-spec info(tab(), item()) -> term().
|
||||||
info(Tab, Item) ->
|
info(Tab, Item) ->
|
||||||
case check_access(Tab) of
|
case check_access(Tab) of
|
||||||
undefined ->
|
undefined ->
|
||||||
erlang:error(badarg, [Tab]);
|
erlang:error(badarg, [Tab]);
|
||||||
{Mod, Impl} ->
|
Mod ->
|
||||||
case Item of
|
case Item of
|
||||||
owner ->
|
owner ->
|
||||||
Tab#tab.owner;
|
Tab#tab.owner;
|
||||||
|
@ -345,14 +432,349 @@ info(Tab, Item) ->
|
||||||
async ->
|
async ->
|
||||||
Tab#tab.async;
|
Tab#tab.async;
|
||||||
memory ->
|
memory ->
|
||||||
Mod:info_memory(Tab, Impl);
|
Mod:info_memory(Tab);
|
||||||
size ->
|
size ->
|
||||||
Mod:info_size(Tab, Impl);
|
Mod:info_size(Tab);
|
||||||
_ ->
|
_ ->
|
||||||
erlang:error(badarg, [Tab, Item])
|
erlang:error(badarg, [Tab, Item])
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @doc Inserts the object or all of the objects in the list
|
||||||
|
%% +ObjOrObjs+ into the table +Tab+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:insert/2
|
||||||
|
|
||||||
|
-spec insert(tab(), object() | [object()]) -> true.
|
||||||
|
insert(Tab, ObjOrObjs) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:insert(Tab, ObjOrObjs)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc This function works exactly like +insert/2+, with the
|
||||||
|
%% exception that instead of overwriting objects with the same key, it
|
||||||
|
%% simply returns false. This function only applies to the +ets+
|
||||||
|
%% implementation.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:insert_new/2
|
||||||
|
|
||||||
|
-spec insert_new(tab(), object() | [object()]) -> true.
|
||||||
|
insert_new(Tab, ObjOrObjs) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:insert_new(Tab, ObjOrObjs)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Returns the last key +Key+ in the table +Tab+. If the table
|
||||||
|
%% is empty, +'$end_of_table'+ will be returned.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:last/1
|
||||||
|
|
||||||
|
-spec last(tab()) -> key() | '$end_of_table'.
|
||||||
|
last(Tab) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:last(Tab)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Returns a list of all objects with the key +Key+ in the table
|
||||||
|
%% +Tab+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:lookup/2
|
||||||
|
|
||||||
|
-spec lookup(tab(), key()) -> [object()].
|
||||||
|
lookup(Tab, Key) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:lookup(Tab, Key)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Returns the +Pos+:th element of the object with the key +Key+
|
||||||
|
%% in the table +Tab+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:lookup_element/3
|
||||||
|
|
||||||
|
-spec lookup_element(tab(), key(), pos()) -> term().
|
||||||
|
lookup_element(Tab, Key, Pos) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:lookup_element(Tab, Key, Pos)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Matches the objects in the table +Tab+ against the pattern
|
||||||
|
%% +Pattern+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:match/2
|
||||||
|
|
||||||
|
-spec match(tab(), pattern()) -> [match()].
|
||||||
|
match(Tab, Pattern) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:match(Tab, Pattern)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Matches the objects in the table +Tab+ against the pattern
|
||||||
|
%% +Pattern+ and returns a limited (+Limit+) number of matching
|
||||||
|
%% objects.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:match/3
|
||||||
|
|
||||||
|
-spec match(tab(), pattern(), limit()) -> {[match()],cont()} | '$end_of_table'.
|
||||||
|
match(Tab, Pattern, Limit) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
wrap_cont_reply(Tab, Mod:match(Tab, Pattern, Limit))
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Continues a match started with +match/3+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:match/1
|
||||||
|
|
||||||
|
-spec match(cont()) -> {[match()],cont()} | '$end_of_table'.
|
||||||
|
match({cont, Tab, Cont}) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
wrap_cont_reply(Tab, Mod:match(Cont))
|
||||||
|
end;
|
||||||
|
match('$end_of_table') ->
|
||||||
|
'$end_of_table'.
|
||||||
|
|
||||||
|
%% @doc Deletes all objects which match the pattern +Pattern+ from the
|
||||||
|
%% table +Tab+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:match_delete/2
|
||||||
|
|
||||||
|
-spec match_delete(tab(), pattern()) -> true.
|
||||||
|
match_delete(Tab, Pattern) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:match_delete(Tab, Pattern)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Matches the objects in the table +Tab+ against the pattern
|
||||||
|
%% +Pattern+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:match_object/2
|
||||||
|
|
||||||
|
-spec match_object(tab(), pattern()) -> [match()].
|
||||||
|
match_object(Tab, Pattern) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:match_object(Tab, Pattern)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Matches the objects in the table +Tab+ against the pattern
|
||||||
|
%% +Pattern+ and returns a limited (+Limit+) number of matching
|
||||||
|
%% objects.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:match_object/3
|
||||||
|
|
||||||
|
-spec match_object(tab(), pattern(), limit()) -> {[match()], cont()} | '$end_of_table'.
|
||||||
|
match_object(Tab, Pattern, Limit) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
wrap_cont_reply(Tab, Mod:match_object(Tab, Pattern, Limit))
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Continues a match started with +match_object/3+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:match_object/1
|
||||||
|
|
||||||
|
-spec match_object(cont()) -> {[match()], cont()} | '$end_of_table'.
|
||||||
|
match_object({cont, Tab, Cont}) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
wrap_cont_reply(Tab, Mod:match_object(Cont))
|
||||||
|
end;
|
||||||
|
match_object('$end_of_table') ->
|
||||||
|
'$end_of_table'.
|
||||||
|
|
||||||
|
%% @doc Returns +true+ if one or more elements in the table +Tab+ has
|
||||||
|
%% the key +Key+, +false+ otherwise.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:member/2
|
||||||
|
|
||||||
|
-spec member(tab(), key()) -> true | false.
|
||||||
|
member(Tab, Key) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:member(Tab, Key)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Returns the next key +Key2+, following the key +Key1+ in the
|
||||||
|
%% table +Tab+. If there is no next key, +'$end_of_table'+ is
|
||||||
|
%% returned.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:next/2
|
||||||
|
|
||||||
|
-spec next(tab(), key()) -> key() | '$end_of_table'.
|
||||||
|
next(Tab, Key) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:next(Tab, Key)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Returns the previous key +Key2+, following the key +Key1+ in
|
||||||
|
%% the table +Tab+. If there is no previous key, +'$end_of_table'+ is
|
||||||
|
%% returned.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:prev/2
|
||||||
|
|
||||||
|
-spec prev(tab(), key()) -> key() | '$end_of_table'.
|
||||||
|
prev(Tab, Key) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:prev(Tab, Key)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% repair_continuation/2
|
||||||
|
|
||||||
|
%% @doc Matches the objects in the table +Tab+ against the spec
|
||||||
|
%% +Spec+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:select/2
|
||||||
|
|
||||||
|
-spec select(tab(), spec()) -> [match()].
|
||||||
|
select(Tab, Spec) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:select(Tab, Spec)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Matches the objects in the table +Tab+ against the spec +Spec+
|
||||||
|
%% and returns a limited (+Limit+) number of matching objects.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:select/3
|
||||||
|
|
||||||
|
-spec select(tab(), spec(), limit()) -> {[match()], cont()} | '$end_of_table'.
|
||||||
|
select(Tab, Spec, Limit) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
wrap_cont_reply(Tab, Mod:select(Tab, Spec, Limit))
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Continues a select started with +select/3+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:select/1
|
||||||
|
|
||||||
|
-spec select(cont()) -> {[match()], cont()} | '$end_of_table'.
|
||||||
|
select({cont, Tab, Cont}) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
wrap_cont_reply(Tab, Mod:select(Cont))
|
||||||
|
end;
|
||||||
|
select('$end_of_table') ->
|
||||||
|
'$end_of_table'.
|
||||||
|
|
||||||
|
%% @doc Counts all objects which match the spec +Spec+ from the
|
||||||
|
%% table +Tab+ and returns the number matched.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:select_count/2
|
||||||
|
|
||||||
|
-spec select_count(tab(), pattern()) -> pos_integer().
|
||||||
|
select_count(Tab, Spec) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:select_count(Tab, Spec)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Deletes all objects which match the spec +Spec+ from the
|
||||||
|
%% table +Tab+ and returns the number deleted.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:select_delete/2
|
||||||
|
|
||||||
|
-spec select_delete(tab(), pattern()) -> pos_integer().
|
||||||
|
select_delete(Tab, Spec) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:select_delete(Tab, Spec)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Matches in reverse the objects in the table +Tab+ against the
|
||||||
|
%% spec +Spec+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:select_reverse/2
|
||||||
|
|
||||||
|
-spec select_reverse(tab(), spec()) -> [match()].
|
||||||
|
select_reverse(Tab, Spec) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
Mod:select_reverse(Tab, Spec)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Matches in reverse the objects in the table +Tab+ against the
|
||||||
|
%% spec +Spec+ and returns a limited (+Limit+) number of matching
|
||||||
|
%% objects.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:select_reverse/3
|
||||||
|
|
||||||
|
-spec select_reverse(tab(), spec(), limit()) -> {[match()], cont()} | '$end_of_table'.
|
||||||
|
select_reverse(Tab, Spec, Limit) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
wrap_cont_reply(Tab, Mod:select_reverse(Tab, Spec, Limit))
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Continues a select reverse started with +select_reverse/3+.
|
||||||
|
%% @end
|
||||||
|
%% @see ets:select_reverse/1
|
||||||
|
|
||||||
|
-spec select_reverse(cont()) -> {[match()], cont()} | '$end_of_table'.
|
||||||
|
select_reverse({cont, Tab, Cont}) ->
|
||||||
|
case check_access(Tab) of
|
||||||
|
undefined ->
|
||||||
|
erlang:error(badarg, [Tab]);
|
||||||
|
Mod ->
|
||||||
|
wrap_cont_reply(Tab, Mod:select_reverse(Cont))
|
||||||
|
end;
|
||||||
|
select_reverse('$end_of_table') ->
|
||||||
|
'$end_of_table'.
|
||||||
|
|
||||||
%% @doc Returns a list of all objects in the table +Tab+. The
|
%% @doc Returns a list of all objects in the table +Tab+. The
|
||||||
%% operation is *not* guaranteed to be atomic and isolated.
|
%% operation is *not* guaranteed to be atomic and isolated.
|
||||||
%% @end
|
%% @end
|
||||||
|
@ -363,8 +785,8 @@ tab2list(Tab) ->
|
||||||
case check_access(Tab) of
|
case check_access(Tab) of
|
||||||
undefined ->
|
undefined ->
|
||||||
erlang:error(badarg, [Tab]);
|
erlang:error(badarg, [Tab]);
|
||||||
{Mod, Impl} ->
|
Mod ->
|
||||||
Mod:tab2list(Tab, Impl)
|
Mod:tab2list(Tab)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
@ -372,16 +794,17 @@ tab2list(Tab) ->
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
check_access(#tab{impl=undefined}) ->
|
||||||
check_access(#tab{protection=Protection, owner=Owner, drv=Drv, nif=undefined, ets=undefined})
|
undefined;
|
||||||
|
check_access(#tab{protection=Protection, owner=Owner, impl=Impl})
|
||||||
when Protection==public orelse Owner==self() ->
|
when Protection==public orelse Owner==self() ->
|
||||||
{lets_drv, Drv};
|
if is_port(Impl) ->
|
||||||
check_access(#tab{protection=Protection, owner=Owner, drv=undefined, nif=Nif, ets=undefined})
|
lets_drv;
|
||||||
when Protection==public orelse Owner==self() ->
|
is_atom(Impl) orelse is_integer(Impl) ->
|
||||||
{lets_nif, Nif};
|
lets_ets;
|
||||||
check_access(#tab{protection=Protection, owner=Owner, drv=undefined, nif=undefined, ets=Ets})
|
true ->
|
||||||
when Protection==public orelse Owner==self() ->
|
lets_nif
|
||||||
{lets_ets, Ets};
|
end;
|
||||||
check_access(_Tab) ->
|
check_access(_Tab) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
@ -525,3 +948,10 @@ sub_options(Key, Value, Options, Keys, L, SubKeys) ->
|
||||||
{_NewValue, _} ->
|
{_NewValue, _} ->
|
||||||
options(Options, Keys, L)
|
options(Options, Keys, L)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
wrap_cont_reply(_Tab, '$end_of_table'=Reply) ->
|
||||||
|
Reply;
|
||||||
|
wrap_cont_reply(_Tab, {_Match, '$end_of_table'}=Reply) ->
|
||||||
|
Reply;
|
||||||
|
wrap_cont_reply(Tab, {_Match, Cont}=Reply) ->
|
||||||
|
setelement(2, Reply, {cont, Tab, Cont}).
|
||||||
|
|
|
@ -33,9 +33,7 @@
|
||||||
protection=protected :: public|protected|private,
|
protection=protected :: public|protected|private,
|
||||||
compressed=false :: boolean(),
|
compressed=false :: boolean(),
|
||||||
async=false :: boolean(),
|
async=false :: boolean(),
|
||||||
drv :: port() | undefined,
|
impl :: port() | nif() | ets:tab() | undefined
|
||||||
nif :: nif() | undefined,
|
|
||||||
ets :: ets:tab() | undefined
|
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-endif. % -ifndef(lets).
|
-endif. % -ifndef(lets).
|
||||||
|
|
508
src/lets_drv.erl
508
src/lets_drv.erl
|
@ -28,17 +28,46 @@
|
||||||
-export([open/4
|
-export([open/4
|
||||||
, destroy/4
|
, destroy/4
|
||||||
, repair/4
|
, repair/4
|
||||||
, insert/3
|
, delete/1
|
||||||
, insert_new/3
|
|
||||||
, delete/2
|
, delete/2
|
||||||
, delete/3
|
, delete_all_objects/1
|
||||||
, delete_all_objects/2
|
, first/1
|
||||||
, lookup/3
|
%% , first_iter/1
|
||||||
, first/2
|
, foldl/3
|
||||||
, next/3
|
, foldr/3
|
||||||
, info_memory/2
|
%% , nfoldl/4
|
||||||
, info_size/2
|
%% , nfoldr/4
|
||||||
, tab2list/2
|
%% , nfoldl/1
|
||||||
|
%% , nfoldr/1
|
||||||
|
, info_memory/1
|
||||||
|
, info_size/1
|
||||||
|
, insert/2
|
||||||
|
, insert_new/2
|
||||||
|
, last/1
|
||||||
|
%% , last_iter/1
|
||||||
|
, lookup/2
|
||||||
|
, lookup_element/3
|
||||||
|
, match/2
|
||||||
|
, match/3
|
||||||
|
, match/1
|
||||||
|
, match_delete/2
|
||||||
|
, match_object/2
|
||||||
|
, match_object/3
|
||||||
|
, match_object/1
|
||||||
|
, member/2
|
||||||
|
, next/2
|
||||||
|
%% , next_iter/2
|
||||||
|
, prev/2
|
||||||
|
%% , prev_iter/2
|
||||||
|
, select/2
|
||||||
|
, select/3
|
||||||
|
, select/1
|
||||||
|
, select_count/2
|
||||||
|
, select_delete/2
|
||||||
|
, select_reverse/2
|
||||||
|
, select_reverse/3
|
||||||
|
, select_reverse/1
|
||||||
|
, tab2list/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,24 +77,32 @@
|
||||||
|
|
||||||
-define(LETS_BADARG, 16#00).
|
-define(LETS_BADARG, 16#00).
|
||||||
-define(LETS_TRUE, 16#01).
|
-define(LETS_TRUE, 16#01).
|
||||||
-define(LETS_END_OF_TABLE, 16#02).
|
-define(LETS_FALSE, 16#02).
|
||||||
-define(LETS_BINARY, 16#03).
|
-define(LETS_END_OF_TABLE, 16#03).
|
||||||
|
-define(LETS_BINARY, 16#04).
|
||||||
|
|
||||||
-define(LETS_OPEN6, 16#00).
|
-define(LETS_OPEN6, 16#00).
|
||||||
-define(LETS_DESTROY6, 16#01).
|
-define(LETS_DESTROY6, 16#01).
|
||||||
-define(LETS_REPAIR6, 16#02).
|
-define(LETS_REPAIR6, 16#02).
|
||||||
-define(LETS_INSERT2, 16#03).
|
-define(LETS_DELETE1, 16#03).
|
||||||
-define(LETS_INSERT3, 16#04).
|
-define(LETS_DELETE2, 16#04).
|
||||||
-define(LETS_INSERT_NEW2, 16#05).
|
-define(LETS_DELETE_ALL_OBJECTS1, 16#05).
|
||||||
-define(LETS_INSERT_NEW3, 16#06).
|
-define(LETS_FIRST1, 16#06).
|
||||||
-define(LETS_DELETE1, 16#07).
|
-define(LETS_FIRST_ITER1, 16#07).
|
||||||
-define(LETS_DELETE2, 16#08).
|
-define(LETS_INFO_MEMORY1, 16#08).
|
||||||
-define(LETS_DELETE_ALL_OBJECTS1, 16#09).
|
-define(LETS_INFO_SIZE1, 16#09).
|
||||||
-define(LETS_LOOKUP2, 16#0A).
|
-define(LETS_INSERT2, 16#0A).
|
||||||
-define(LETS_FIRST1, 16#0B).
|
-define(LETS_INSERT3, 16#0B).
|
||||||
-define(LETS_NEXT2, 16#0C).
|
-define(LETS_INSERT_NEW2, 16#0C).
|
||||||
-define(LETS_INFO_MEMORY1, 16#0D).
|
-define(LETS_INSERT_NEW3, 16#0D).
|
||||||
-define(LETS_INFO_SIZE1, 16#0E).
|
-define(LETS_LAST1, 16#0E).
|
||||||
|
-define(LETS_LAST_ITER1, 16#0F).
|
||||||
|
-define(LETS_LOOKUP2, 16#10).
|
||||||
|
-define(LETS_MEMBER2, 16#11).
|
||||||
|
-define(LETS_NEXT2, 16#12).
|
||||||
|
-define(LETS_NEXT_ITER2, 16#13).
|
||||||
|
-define(LETS_PREV2, 16#14).
|
||||||
|
-define(LETS_PREV_ITER2, 16#15).
|
||||||
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
@ -99,7 +136,7 @@ open(#tab{name=_Name, named_table=_Named, type=Type, protection=Protection}=Tab,
|
||||||
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
||||||
Impl = impl_open(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions),
|
Impl = impl_open(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions),
|
||||||
%% @TODO implement named Impl (of sorts)
|
%% @TODO implement named Impl (of sorts)
|
||||||
Tab#tab{drv=Impl}.
|
Tab#tab{impl=Impl}.
|
||||||
|
|
||||||
destroy(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOptions) ->
|
destroy(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOptions) ->
|
||||||
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
||||||
|
@ -109,40 +146,16 @@ repair(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOption
|
||||||
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
||||||
impl_repair(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions).
|
impl_repair(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions).
|
||||||
|
|
||||||
insert(#tab{keypos=KeyPos, type=Type}, Impl, Object) when is_tuple(Object) ->
|
delete(#tab{impl=Impl}) ->
|
||||||
Key = element(KeyPos,Object),
|
|
||||||
Val = Object,
|
|
||||||
impl_insert(Impl, encode(Type, Key), encode(Type, Val));
|
|
||||||
insert(#tab{keypos=KeyPos, type=Type}, Impl, Objects) when is_list(Objects) ->
|
|
||||||
List = [{encode(Type, element(KeyPos,Object)), encode(Type, Object)} || Object <- Objects ],
|
|
||||||
impl_insert(Impl, List).
|
|
||||||
|
|
||||||
insert_new(#tab{keypos=KeyPos, type=Type}, Impl, Object) when is_tuple(Object) ->
|
|
||||||
Key = element(KeyPos,Object),
|
|
||||||
Val = Object,
|
|
||||||
impl_insert_new(Impl, encode(Type, Key), encode(Type, Val));
|
|
||||||
insert_new(#tab{keypos=KeyPos, type=Type}, Impl, Objects) when is_list(Objects) ->
|
|
||||||
List = [{encode(Type, element(KeyPos,Object)), encode(Type, Object)} || Object <- Objects ],
|
|
||||||
impl_insert_new(Impl, List).
|
|
||||||
|
|
||||||
delete(_Tab, Impl) ->
|
|
||||||
impl_delete(Impl).
|
impl_delete(Impl).
|
||||||
|
|
||||||
delete(#tab{type=Type}, Impl, Key) ->
|
delete(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
impl_delete(Impl, encode(Type, Key)).
|
impl_delete(Impl, encode(Type, Key)).
|
||||||
|
|
||||||
delete_all_objects(_Tab, Impl) ->
|
delete_all_objects(#tab{impl=Impl}) ->
|
||||||
impl_delete_all_objects(Impl).
|
impl_delete_all_objects(Impl).
|
||||||
|
|
||||||
lookup(#tab{type=Type}, Impl, Key) ->
|
first(#tab{type=Type, impl=Impl}) ->
|
||||||
case impl_lookup(Impl, encode(Type, Key)) of
|
|
||||||
'$end_of_table' ->
|
|
||||||
[];
|
|
||||||
Object when is_binary(Object) ->
|
|
||||||
[decode(Type, Object)]
|
|
||||||
end.
|
|
||||||
|
|
||||||
first(#tab{type=Type}, Impl) ->
|
|
||||||
case impl_first(Impl) of
|
case impl_first(Impl) of
|
||||||
'$end_of_table' ->
|
'$end_of_table' ->
|
||||||
'$end_of_table';
|
'$end_of_table';
|
||||||
|
@ -150,15 +163,31 @@ first(#tab{type=Type}, Impl) ->
|
||||||
decode(Type, Key)
|
decode(Type, Key)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
next(#tab{type=Type}, Impl, Key) ->
|
first_iter(#tab{type=Type, impl=Impl}) ->
|
||||||
case impl_next(Impl, encode(Type, Key)) of
|
case impl_first_iter(Impl) of
|
||||||
'$end_of_table' ->
|
'$end_of_table' ->
|
||||||
'$end_of_table';
|
'$end_of_table';
|
||||||
Next ->
|
Key ->
|
||||||
decode(Type, Next)
|
decode(Type, Key)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
info_memory(_Tab, Impl) ->
|
last(#tab{type=Type, impl=Impl}) ->
|
||||||
|
case impl_last(Impl) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Key ->
|
||||||
|
decode(Type, Key)
|
||||||
|
end.
|
||||||
|
|
||||||
|
last_iter(#tab{type=Type, impl=Impl}) ->
|
||||||
|
case impl_last_iter(Impl) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Key ->
|
||||||
|
decode(Type, Key)
|
||||||
|
end.
|
||||||
|
|
||||||
|
info_memory(#tab{impl=Impl}) ->
|
||||||
case impl_info_memory(Impl) of
|
case impl_info_memory(Impl) of
|
||||||
Memory when is_integer(Memory) ->
|
Memory when is_integer(Memory) ->
|
||||||
erlang:round(Memory / erlang:system_info(wordsize));
|
erlang:round(Memory / erlang:system_info(wordsize));
|
||||||
|
@ -166,30 +195,286 @@ info_memory(_Tab, Impl) ->
|
||||||
Else
|
Else
|
||||||
end.
|
end.
|
||||||
|
|
||||||
info_size(_Tab, Impl) ->
|
info_size(#tab{impl=Impl}) ->
|
||||||
impl_info_size(Impl).
|
impl_info_size(Impl).
|
||||||
|
|
||||||
tab2list(Tab, Impl) ->
|
insert(#tab{keypos=KeyPos, type=Type, impl=Impl}, Object) when is_tuple(Object) ->
|
||||||
tab2list(Tab, Impl, impl_first(Impl), []).
|
Key = element(KeyPos, Object),
|
||||||
|
Val = Object,
|
||||||
|
impl_insert(Impl, encode(Type, Key), encode(Type, Val));
|
||||||
|
insert(#tab{keypos=KeyPos, type=Type, impl=Impl}, Objects) when is_list(Objects) ->
|
||||||
|
List = [{encode(Type, element(KeyPos, Object)), encode(Type, Object)} || Object <- Objects ],
|
||||||
|
impl_insert(Impl, List).
|
||||||
|
|
||||||
tab2list(_Tab, _Impl, '$end_of_table', Acc) ->
|
insert_new(#tab{keypos=KeyPos, type=Type, impl=Impl}, Object) when is_tuple(Object) ->
|
||||||
lists:reverse(Acc);
|
Key = element(KeyPos, Object),
|
||||||
tab2list(#tab{type=Type}=Tab, Impl, Key, Acc) ->
|
Val = Object,
|
||||||
NewAcc =
|
impl_insert_new(Impl, encode(Type, Key), encode(Type, Val));
|
||||||
case impl_lookup(Impl, Key) of
|
insert_new(#tab{keypos=KeyPos, type=Type, impl=Impl}, Objects) when is_list(Objects) ->
|
||||||
|
List = [{encode(Type, element(KeyPos, Object)), encode(Type, Object)} || Object <- Objects ],
|
||||||
|
impl_insert_new(Impl, List).
|
||||||
|
|
||||||
|
lookup(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_lookup(Impl, encode(Type, Key)) of
|
||||||
'$end_of_table' ->
|
'$end_of_table' ->
|
||||||
%% @NOTE This is not an atomic operation
|
[];
|
||||||
Acc;
|
|
||||||
Object when is_binary(Object) ->
|
Object when is_binary(Object) ->
|
||||||
[decode(Type, Object)|Acc]
|
[decode(Type, Object)]
|
||||||
|
end.
|
||||||
|
|
||||||
|
lookup_element(#tab{type=Type, impl=Impl}, Key, Pos) ->
|
||||||
|
Element =
|
||||||
|
case impl_lookup(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Object when is_binary(Object) ->
|
||||||
|
decode(Type, Object)
|
||||||
end,
|
end,
|
||||||
tab2list(Tab, Impl, impl_next(Impl, Key), NewAcc).
|
element(Pos, Element).
|
||||||
|
|
||||||
|
member(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
impl_member(Impl, encode(Type, Key)).
|
||||||
|
|
||||||
|
next(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_next(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Next ->
|
||||||
|
decode(Type, Next)
|
||||||
|
end.
|
||||||
|
|
||||||
|
next_iter(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_next_iter(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Next ->
|
||||||
|
decode(Type, Next)
|
||||||
|
end.
|
||||||
|
|
||||||
|
prev(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_prev(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Prev ->
|
||||||
|
decode(Type, Prev)
|
||||||
|
end.
|
||||||
|
|
||||||
|
prev_iter(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_prev_iter(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Prev ->
|
||||||
|
decode(Type, Prev)
|
||||||
|
end.
|
||||||
|
|
||||||
|
foldl(Fun, Acc0, Tab) ->
|
||||||
|
foldl(Fun, Acc0, Tab, first_iter(Tab)).
|
||||||
|
|
||||||
|
foldr(Fun, Acc0, Tab) ->
|
||||||
|
foldr(Fun, Acc0, Tab, last_iter(Tab)).
|
||||||
|
|
||||||
|
nfoldl(Fun, Acc0, Tab, Limit) when Limit > 0 ->
|
||||||
|
nfoldl(Fun, Acc0, Acc0, Tab, Limit, Limit, first_iter(Tab));
|
||||||
|
nfoldl(_Fun, _Acc0, _Tab, Limit) ->
|
||||||
|
exit({badarg,Limit}).
|
||||||
|
|
||||||
|
nfoldl('$end_of_table') ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldl({_Fun, _Acc0, _Tab, _Limit0, '$end_of_table'}) ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldl({Fun, Acc0, Tab, Limit0, Key}) ->
|
||||||
|
nfoldl(Fun, Acc0, Acc0, Tab, Limit0, Limit0, next_iter(Tab, Key)).
|
||||||
|
|
||||||
|
nfoldr(Fun, Acc0, Tab, Limit) when Limit > 0 ->
|
||||||
|
nfoldr(Fun, Acc0, Acc0, Tab, Limit, Limit, last_iter(Tab));
|
||||||
|
nfoldr(_Fun, _Acc0, _Tab, Limit) ->
|
||||||
|
exit({badarg,Limit}).
|
||||||
|
|
||||||
|
nfoldr('$end_of_table') ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldr({_Fun, _Acc0, _Tab, _Limit0, '$end_of_table'}) ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldr({Fun, Acc0, Tab, Limit0, Key}) ->
|
||||||
|
nfoldr(Fun, Acc0, Acc0, Tab, Limit0, Limit0, prev_iter(Tab, Key)).
|
||||||
|
|
||||||
|
tab2list(Tab) ->
|
||||||
|
foldr(fun(X, Acc) -> [X|Acc] end, [], Tab).
|
||||||
|
|
||||||
|
match(Tab, Pattern) ->
|
||||||
|
select(Tab, [{Pattern, [], ['$$']}]).
|
||||||
|
|
||||||
|
match(Tab, Pattern, Limit) ->
|
||||||
|
select(Tab, [{Pattern, [], ['$$']}], Limit).
|
||||||
|
|
||||||
|
match(Cont) ->
|
||||||
|
select(Cont).
|
||||||
|
|
||||||
|
match_delete(Tab, Pattern) ->
|
||||||
|
select_delete(Tab, [{Pattern, [], [true]}]),
|
||||||
|
true.
|
||||||
|
|
||||||
|
match_object(Tab, Pattern) ->
|
||||||
|
select(Tab, [{Pattern, [], ['$_']}]).
|
||||||
|
|
||||||
|
match_object(Tab, Pattern, Limit) ->
|
||||||
|
select(Tab, [{Pattern, [], ['$_']}], Limit).
|
||||||
|
|
||||||
|
match_object(Cont) ->
|
||||||
|
select(Cont).
|
||||||
|
|
||||||
|
select(Tab, Spec) ->
|
||||||
|
Fun = fun(_Object, Match, Acc) -> [Match|Acc] end,
|
||||||
|
selectr(Fun, [], Tab, Spec).
|
||||||
|
|
||||||
|
select(Tab, Spec, Limit) ->
|
||||||
|
Fun = fun(_Object, Match, Acc) -> [Match|Acc] end,
|
||||||
|
case nselectl(Fun, [], Tab, Spec, Limit) of
|
||||||
|
{Acc, Cont} ->
|
||||||
|
{lists:reverse(Acc), Cont};
|
||||||
|
Cont ->
|
||||||
|
Cont
|
||||||
|
end.
|
||||||
|
|
||||||
|
select(Cont0) ->
|
||||||
|
case nselectl(Cont0) of
|
||||||
|
{Acc, Cont} ->
|
||||||
|
{lists:reverse(Acc), Cont};
|
||||||
|
Cont ->
|
||||||
|
Cont
|
||||||
|
end.
|
||||||
|
|
||||||
|
select_count(Tab, Spec) ->
|
||||||
|
Fun = fun(_Object, true, Acc) ->
|
||||||
|
Acc + 1;
|
||||||
|
(_Object, _Match, Acc) ->
|
||||||
|
Acc
|
||||||
|
end,
|
||||||
|
selectl(Fun, 0, Tab, Spec).
|
||||||
|
|
||||||
|
select_delete(#tab{keypos=KeyPos}=Tab, Spec) ->
|
||||||
|
Fun = fun(Object, true, Acc) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
delete(Tab, Key),
|
||||||
|
Acc + 1;
|
||||||
|
(_Object, _Match, Acc) ->
|
||||||
|
Acc
|
||||||
|
end,
|
||||||
|
selectl(Fun, 0, Tab, Spec).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec) ->
|
||||||
|
Fun = fun(_Object, Match, Acc) -> [Match|Acc] end,
|
||||||
|
selectl(Fun, [], Tab, Spec).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec, Limit) ->
|
||||||
|
Fun = fun(_Object, Match, Acc) -> [Match|Acc] end,
|
||||||
|
case nselectr(Fun, [], Tab, Spec, Limit) of
|
||||||
|
{Acc, Cont} ->
|
||||||
|
{lists:reverse(Acc), Cont};
|
||||||
|
Cont ->
|
||||||
|
Cont
|
||||||
|
end.
|
||||||
|
|
||||||
|
select_reverse(Cont0) ->
|
||||||
|
case nselectr(Cont0) of
|
||||||
|
{Acc, Cont} ->
|
||||||
|
{lists:reverse(Acc), Cont};
|
||||||
|
Cont ->
|
||||||
|
Cont
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
foldl(_Fun, Acc, _Tab, '$end_of_table') ->
|
||||||
|
Acc;
|
||||||
|
foldl(Fun, Acc, #tab{keypos=KeyPos}=Tab, Object) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
foldl(Fun, Fun(Object, Acc), Tab, next_iter(Tab, Key)).
|
||||||
|
|
||||||
|
foldr(_Fun, Acc, _Tab, '$end_of_table') ->
|
||||||
|
Acc;
|
||||||
|
foldr(Fun, Acc, #tab{keypos=KeyPos}=Tab, Object) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
foldr(Fun, Fun(Object, Acc), Tab, prev_iter(Tab, Key)).
|
||||||
|
|
||||||
|
nfoldl(_Fun, Acc0, Acc0, _Tab, _Limit0, _Limit, '$end_of_table') ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldl(_Fun, _Acc0, Acc, _Tab, _Limit0, _Limit, '$end_of_table'=Cont) ->
|
||||||
|
{Acc, Cont};
|
||||||
|
nfoldl(Fun, Acc0, Acc, #tab{keypos=KeyPos}=Tab, Limit0, Limit, Object) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
case Fun(Object, Acc) of
|
||||||
|
{true, NewAcc} ->
|
||||||
|
if Limit > 1 ->
|
||||||
|
nfoldl(Fun, Acc0, NewAcc, Tab, Limit0, Limit-1, next_iter(Tab, Key));
|
||||||
|
true ->
|
||||||
|
Cont = {Fun, Acc0, Tab, Limit0, Key},
|
||||||
|
{NewAcc, Cont}
|
||||||
|
end;
|
||||||
|
{false, NewAcc} ->
|
||||||
|
nfoldl(Fun, Acc0, NewAcc, Tab, Limit0, Limit, next_iter(Tab, Key))
|
||||||
|
end.
|
||||||
|
|
||||||
|
nfoldr(_Fun, Acc0, Acc0, _Tab, _Limit0, _Limit, '$end_of_table') ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldr(_Fun, _Acc0, Acc, _Tab, _Limit0, _Limit, '$end_of_table'=Cont) ->
|
||||||
|
{Acc, Cont};
|
||||||
|
nfoldr(Fun, Acc0, Acc, #tab{keypos=KeyPos}=Tab, Limit0, Limit, Object) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
case Fun(Object, Acc) of
|
||||||
|
{true, NewAcc} ->
|
||||||
|
if Limit > 1 ->
|
||||||
|
nfoldr(Fun, Acc0, NewAcc, Tab, Limit0, Limit-1, prev_iter(Tab, Key));
|
||||||
|
true ->
|
||||||
|
Cont = {Fun, Acc0, Tab, Limit0, Key},
|
||||||
|
{NewAcc, Cont}
|
||||||
|
end;
|
||||||
|
{false, NewAcc} ->
|
||||||
|
nfoldr(Fun, Acc0, NewAcc, Tab, Limit0, Limit, prev_iter(Tab, Key))
|
||||||
|
end.
|
||||||
|
|
||||||
|
selectl(Fun, Acc0, Tab, Spec) ->
|
||||||
|
foldl(selectfun(Fun, Spec), Acc0, Tab).
|
||||||
|
|
||||||
|
selectr(Fun, Acc0, Tab, Spec) ->
|
||||||
|
foldr(selectfun(Fun, Spec), Acc0, Tab).
|
||||||
|
|
||||||
|
nselectl(Fun, Acc0, Tab, Spec, Limit0) ->
|
||||||
|
nfoldl(nselectfun(Fun, Spec), Acc0, Tab, Limit0).
|
||||||
|
|
||||||
|
nselectr(Fun, Acc0, Tab, Spec, Limit0) ->
|
||||||
|
nfoldr(nselectfun(Fun, Spec), Acc0, Tab, Limit0).
|
||||||
|
|
||||||
|
nselectl(Cont) ->
|
||||||
|
nfoldl(Cont).
|
||||||
|
|
||||||
|
nselectr(Cont) ->
|
||||||
|
nfoldr(Cont).
|
||||||
|
|
||||||
|
selectfun(Fun, Spec) ->
|
||||||
|
CMSpec = ets:match_spec_compile(Spec),
|
||||||
|
fun(Object, Acc) ->
|
||||||
|
case ets:match_spec_run([Object], CMSpec) of
|
||||||
|
[] ->
|
||||||
|
Acc;
|
||||||
|
[Match] ->
|
||||||
|
Fun(Object, Match, Acc)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
nselectfun(Fun, Spec) ->
|
||||||
|
CMSpec = ets:match_spec_compile(Spec),
|
||||||
|
fun(Object, Acc) ->
|
||||||
|
case ets:match_spec_run([Object], CMSpec) of
|
||||||
|
[] ->
|
||||||
|
{false, Acc};
|
||||||
|
[Match] ->
|
||||||
|
{true, Fun(Object, Match, Acc)}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
encode(set, Term) ->
|
encode(set, Term) ->
|
||||||
term_to_binary(Term);
|
term_to_binary(Term);
|
||||||
encode(ordered_set, Term) ->
|
encode(ordered_set, Term) ->
|
||||||
|
@ -200,6 +485,22 @@ decode(set, Term) ->
|
||||||
decode(ordered_set, Term) ->
|
decode(ordered_set, Term) ->
|
||||||
sext:decode(Term).
|
sext:decode(Term).
|
||||||
|
|
||||||
|
call(Impl, Tuple) ->
|
||||||
|
Data = term_to_binary(Tuple),
|
||||||
|
port_command(Impl, Data),
|
||||||
|
receive
|
||||||
|
{Impl, ?LETS_BINARY, Reply} ->
|
||||||
|
Reply;
|
||||||
|
{Impl, ?LETS_TRUE} ->
|
||||||
|
true;
|
||||||
|
{Impl, ?LETS_FALSE} ->
|
||||||
|
false;
|
||||||
|
{Impl, ?LETS_END_OF_TABLE} ->
|
||||||
|
'$end_of_table';
|
||||||
|
{Impl, ?LETS_BADARG} ->
|
||||||
|
erlang:error(badarg, [Impl])
|
||||||
|
end.
|
||||||
|
|
||||||
impl_open(Type, Protection, Path, Options, ReadOptions, WriteOptions) ->
|
impl_open(Type, Protection, Path, Options, ReadOptions, WriteOptions) ->
|
||||||
Impl = init(),
|
Impl = init(),
|
||||||
true = call(Impl, {?LETS_OPEN6, Type, Protection, Path, Options, ReadOptions, WriteOptions}),
|
true = call(Impl, {?LETS_OPEN6, Type, Protection, Path, Options, ReadOptions, WriteOptions}),
|
||||||
|
@ -219,18 +520,6 @@ impl_repair(Type, Protection, Path, Options, ReadOptions, WriteOptions) ->
|
||||||
_ = erl_ddll:unload(lets_drv),
|
_ = erl_ddll:unload(lets_drv),
|
||||||
true.
|
true.
|
||||||
|
|
||||||
impl_insert(Impl, Key, Object) ->
|
|
||||||
call(Impl, {?LETS_INSERT3, Key, Object}).
|
|
||||||
|
|
||||||
impl_insert(Impl, List) ->
|
|
||||||
call(Impl, {?LETS_INSERT2, List}).
|
|
||||||
|
|
||||||
impl_insert_new(Impl, Key, Object) ->
|
|
||||||
call(Impl, {?LETS_INSERT_NEW3, Key, Object}).
|
|
||||||
|
|
||||||
impl_insert_new(Impl, List) ->
|
|
||||||
call(Impl, {?LETS_INSERT_NEW2, List}).
|
|
||||||
|
|
||||||
impl_delete(Impl) ->
|
impl_delete(Impl) ->
|
||||||
Res = call(Impl, {?LETS_DELETE1}),
|
Res = call(Impl, {?LETS_DELETE1}),
|
||||||
_ = port_close(Impl),
|
_ = port_close(Impl),
|
||||||
|
@ -243,14 +532,17 @@ impl_delete(Impl, Key) ->
|
||||||
impl_delete_all_objects(Impl) ->
|
impl_delete_all_objects(Impl) ->
|
||||||
call(Impl, {?LETS_DELETE_ALL_OBJECTS1}).
|
call(Impl, {?LETS_DELETE_ALL_OBJECTS1}).
|
||||||
|
|
||||||
impl_lookup(Impl, Key) ->
|
|
||||||
call(Impl, {?LETS_LOOKUP2, Key}).
|
|
||||||
|
|
||||||
impl_first(Impl) ->
|
impl_first(Impl) ->
|
||||||
call(Impl, {?LETS_FIRST1}).
|
call(Impl, {?LETS_FIRST1}).
|
||||||
|
|
||||||
impl_next(Impl, Key) ->
|
impl_first_iter(Impl) ->
|
||||||
call(Impl, {?LETS_NEXT2, Key}).
|
call(Impl, {?LETS_FIRST_ITER1}).
|
||||||
|
|
||||||
|
impl_last(Impl) ->
|
||||||
|
call(Impl, {?LETS_LAST1}).
|
||||||
|
|
||||||
|
impl_last_iter(Impl) ->
|
||||||
|
call(Impl, {?LETS_LAST_ITER1}).
|
||||||
|
|
||||||
impl_info_memory(Impl) ->
|
impl_info_memory(Impl) ->
|
||||||
call(Impl, {?LETS_INFO_MEMORY1}).
|
call(Impl, {?LETS_INFO_MEMORY1}).
|
||||||
|
@ -258,16 +550,32 @@ impl_info_memory(Impl) ->
|
||||||
impl_info_size(Impl) ->
|
impl_info_size(Impl) ->
|
||||||
call(Impl, {?LETS_INFO_SIZE1}).
|
call(Impl, {?LETS_INFO_SIZE1}).
|
||||||
|
|
||||||
call(Impl, Tuple) ->
|
impl_insert(Impl, Key, Object) ->
|
||||||
Data = term_to_binary(Tuple),
|
call(Impl, {?LETS_INSERT3, Key, Object}).
|
||||||
port_command(Impl, Data),
|
|
||||||
receive
|
impl_insert(Impl, List) ->
|
||||||
{Impl, ?LETS_BINARY, Reply} ->
|
call(Impl, {?LETS_INSERT2, List}).
|
||||||
Reply;
|
|
||||||
{Impl, ?LETS_TRUE} ->
|
impl_insert_new(Impl, Key, Object) ->
|
||||||
true;
|
call(Impl, {?LETS_INSERT_NEW3, Key, Object}).
|
||||||
{Impl, ?LETS_END_OF_TABLE} ->
|
|
||||||
'$end_of_table';
|
impl_insert_new(Impl, List) ->
|
||||||
{Impl, ?LETS_BADARG} ->
|
call(Impl, {?LETS_INSERT_NEW2, List}).
|
||||||
erlang:error(badarg, [Impl])
|
|
||||||
end.
|
impl_lookup(Impl, Key) ->
|
||||||
|
call(Impl, {?LETS_LOOKUP2, Key}).
|
||||||
|
|
||||||
|
impl_member(Impl, Key) ->
|
||||||
|
call(Impl, {?LETS_MEMBER2, Key}).
|
||||||
|
|
||||||
|
impl_next(Impl, Key) ->
|
||||||
|
call(Impl, {?LETS_NEXT2, Key}).
|
||||||
|
|
||||||
|
impl_next_iter(Impl, Key) ->
|
||||||
|
call(Impl, {?LETS_NEXT_ITER2, Key}).
|
||||||
|
|
||||||
|
impl_prev(Impl, Key) ->
|
||||||
|
call(Impl, {?LETS_PREV2, Key}).
|
||||||
|
|
||||||
|
impl_prev_iter(Impl, Key) ->
|
||||||
|
call(Impl, {?LETS_PREV_ITER2, Key}).
|
||||||
|
|
152
src/lets_ets.erl
152
src/lets_ets.erl
|
@ -28,17 +28,38 @@
|
||||||
-export([open/1
|
-export([open/1
|
||||||
, destroy/1
|
, destroy/1
|
||||||
, repair/1
|
, repair/1
|
||||||
, insert/3
|
, delete/1
|
||||||
, insert_new/3
|
|
||||||
, delete/2
|
, delete/2
|
||||||
, delete/3
|
, delete_all_objects/1
|
||||||
, delete_all_objects/2
|
, first/1
|
||||||
, lookup/3
|
, foldl/3
|
||||||
, first/2
|
, foldr/3
|
||||||
, next/3
|
, info_memory/1
|
||||||
, info_memory/2
|
, info_size/1
|
||||||
, info_size/2
|
, insert/2
|
||||||
, tab2list/2
|
, insert_new/2
|
||||||
|
, last/1
|
||||||
|
, lookup/2
|
||||||
|
, lookup_element/3
|
||||||
|
, match/2
|
||||||
|
, match/3
|
||||||
|
, match/1
|
||||||
|
, match_delete/2
|
||||||
|
, match_object/2
|
||||||
|
, match_object/3
|
||||||
|
, match_object/1
|
||||||
|
, member/2
|
||||||
|
, next/2
|
||||||
|
, prev/2
|
||||||
|
, select/2
|
||||||
|
, select/3
|
||||||
|
, select/1
|
||||||
|
, select_count/2
|
||||||
|
, select_delete/2
|
||||||
|
, select_reverse/2
|
||||||
|
, select_reverse/3
|
||||||
|
, select_reverse/1
|
||||||
|
, tab2list/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,8 +77,8 @@ open(#tab{name=Name, named_table=NamedTable, type=Type, keypos=KeyPos, protectio
|
||||||
[Type, {keypos,KeyPos}, Protection] ++
|
[Type, {keypos,KeyPos}, Protection] ++
|
||||||
[named_table || NamedTable ] ++
|
[named_table || NamedTable ] ++
|
||||||
[compressed || Compressed ],
|
[compressed || Compressed ],
|
||||||
Ets = ets:new(Name, Opts),
|
Impl = ets:new(Name, Opts),
|
||||||
Tab#tab{ets=Ets}.
|
Tab#tab{impl=Impl}.
|
||||||
|
|
||||||
destroy(#tab{}) ->
|
destroy(#tab{}) ->
|
||||||
true.
|
true.
|
||||||
|
@ -65,35 +86,98 @@ destroy(#tab{}) ->
|
||||||
repair(#tab{}) ->
|
repair(#tab{}) ->
|
||||||
true.
|
true.
|
||||||
|
|
||||||
insert(_Tab, Ets, ObjectOrObjects) ->
|
delete(#tab{impl=Impl}) ->
|
||||||
ets:insert(Ets, ObjectOrObjects).
|
ets:delete(Impl).
|
||||||
|
|
||||||
insert_new(_Tab, Ets, ObjectOrObjects) ->
|
delete(#tab{impl=Impl}, Key) ->
|
||||||
ets:insert_new(Ets, ObjectOrObjects).
|
ets:delete(Impl, Key).
|
||||||
|
|
||||||
delete(_Tab, Ets) ->
|
delete_all_objects(#tab{impl=Impl}) ->
|
||||||
ets:delete(Ets).
|
ets:delete_all_objects(Impl).
|
||||||
|
|
||||||
delete(_Tab, Ets, Key) ->
|
first(#tab{impl=Impl}) ->
|
||||||
ets:delete(Ets, Key).
|
ets:first(Impl).
|
||||||
|
|
||||||
delete_all_objects(_Tab, Ets) ->
|
foldl(Function, Acc0, #tab{impl=Impl}) ->
|
||||||
ets:delete_all_objects(Ets).
|
ets:foldl(Function, Acc0, Impl).
|
||||||
|
|
||||||
lookup(_Tab, Ets, Key) ->
|
foldr(Function, Acc0, #tab{impl=Impl}) ->
|
||||||
ets:lookup(Ets, Key).
|
ets:foldr(Function, Acc0, Impl).
|
||||||
|
|
||||||
first(_Tab, Ets) ->
|
info_memory(#tab{impl=Impl}) ->
|
||||||
ets:first(Ets).
|
ets:info(Impl, memory).
|
||||||
|
|
||||||
next(_Tab, Ets, Key) ->
|
info_size(#tab{impl=Impl}) ->
|
||||||
ets:next(Ets, Key).
|
ets:info(Impl, size).
|
||||||
|
|
||||||
info_memory(_Tab, Ets) ->
|
insert(#tab{impl=Impl}, ObjOrObjs) ->
|
||||||
ets:info(Ets, memory).
|
ets:insert(Impl, ObjOrObjs).
|
||||||
|
|
||||||
info_size(_Tab, Ets) ->
|
insert_new(#tab{impl=Impl}, ObjOrObjs) ->
|
||||||
ets:info(Ets, size).
|
ets:insert_new(Impl, ObjOrObjs).
|
||||||
|
|
||||||
tab2list(_Tab, Ets) ->
|
last(#tab{impl=Impl}) ->
|
||||||
ets:tab2list(Ets).
|
ets:last(Impl).
|
||||||
|
|
||||||
|
lookup(#tab{impl=Impl}, Key) ->
|
||||||
|
ets:lookup(Impl, Key).
|
||||||
|
|
||||||
|
lookup_element(#tab{impl=Impl}, Key, Pos) ->
|
||||||
|
ets:lookup_element(Impl, Key, Pos).
|
||||||
|
|
||||||
|
match(#tab{impl=Impl}, Pattern) ->
|
||||||
|
ets:match(Impl, Pattern).
|
||||||
|
|
||||||
|
match(#tab{impl=Impl}, Pattern, Limit) ->
|
||||||
|
ets:match(Impl, Pattern, Limit).
|
||||||
|
|
||||||
|
match(Cont) ->
|
||||||
|
ets:match(Cont).
|
||||||
|
|
||||||
|
match_delete(#tab{impl=Impl}, Pattern) ->
|
||||||
|
ets:match_delete(Impl, Pattern).
|
||||||
|
|
||||||
|
match_object(#tab{impl=Impl}, Pattern) ->
|
||||||
|
ets:match_object(Impl, Pattern).
|
||||||
|
|
||||||
|
match_object(#tab{impl=Impl}, Pattern, Limit) ->
|
||||||
|
ets:match_object(Impl, Pattern, Limit).
|
||||||
|
|
||||||
|
match_object(Cont) ->
|
||||||
|
ets:match_object(Cont).
|
||||||
|
|
||||||
|
member(#tab{impl=Impl}, Key) ->
|
||||||
|
ets:member(Impl, Key).
|
||||||
|
|
||||||
|
next(#tab{impl=Impl}, Key) ->
|
||||||
|
ets:next(Impl, Key).
|
||||||
|
|
||||||
|
prev(#tab{impl=Impl}, Key) ->
|
||||||
|
ets:prev(Impl, Key).
|
||||||
|
|
||||||
|
select(#tab{impl=Impl}, Spec) ->
|
||||||
|
ets:select(Impl, Spec).
|
||||||
|
|
||||||
|
select(#tab{impl=Impl}, Spec, Limit) ->
|
||||||
|
ets:select(Impl, Spec, Limit).
|
||||||
|
|
||||||
|
select(Cont) ->
|
||||||
|
ets:select(Cont).
|
||||||
|
|
||||||
|
select_count(#tab{impl=Impl}, Spec) ->
|
||||||
|
ets:select_count(Impl, Spec).
|
||||||
|
|
||||||
|
select_delete(#tab{impl=Impl}, Spec) ->
|
||||||
|
ets:select_delete(Impl, Spec).
|
||||||
|
|
||||||
|
select_reverse(#tab{impl=Impl}, Spec) ->
|
||||||
|
ets:select_reverse(Impl, Spec).
|
||||||
|
|
||||||
|
select_reverse(#tab{impl=Impl}, Spec, Limit) ->
|
||||||
|
ets:select_reverse(Impl, Spec, Limit).
|
||||||
|
|
||||||
|
select_reverse(Cont) ->
|
||||||
|
ets:select_reverse(Cont).
|
||||||
|
|
||||||
|
tab2list(#tab{impl=Impl}) ->
|
||||||
|
ets:tab2list(Impl).
|
||||||
|
|
435
src/lets_nif.erl
435
src/lets_nif.erl
|
@ -28,19 +28,49 @@
|
||||||
-export([open/4
|
-export([open/4
|
||||||
, destroy/4
|
, destroy/4
|
||||||
, repair/4
|
, repair/4
|
||||||
, insert/3
|
, delete/1
|
||||||
, insert_new/3
|
|
||||||
, delete/2
|
, delete/2
|
||||||
, delete/3
|
, delete_all_objects/1
|
||||||
, delete_all_objects/2
|
, first/1
|
||||||
, lookup/3
|
%% , first_iter/1
|
||||||
, first/2
|
, foldl/3
|
||||||
, next/3
|
, foldr/3
|
||||||
, info_memory/2
|
%% , nfoldl/4
|
||||||
, info_size/2
|
%% , nfoldr/4
|
||||||
, tab2list/2
|
%% , nfoldl/1
|
||||||
|
%% , nfoldr/1
|
||||||
|
, info_memory/1
|
||||||
|
, info_size/1
|
||||||
|
, insert/2
|
||||||
|
, insert_new/2
|
||||||
|
, last/1
|
||||||
|
%% , last_iter/1
|
||||||
|
, lookup/2
|
||||||
|
, lookup_element/3
|
||||||
|
, match/2
|
||||||
|
, match/3
|
||||||
|
, match/1
|
||||||
|
, match_delete/2
|
||||||
|
, match_object/2
|
||||||
|
, match_object/3
|
||||||
|
, match_object/1
|
||||||
|
, member/2
|
||||||
|
, next/2
|
||||||
|
%% , next_iter/2
|
||||||
|
, prev/2
|
||||||
|
%% , prev_iter/2
|
||||||
|
, select/2
|
||||||
|
, select/3
|
||||||
|
, select/1
|
||||||
|
, select_count/2
|
||||||
|
, select_delete/2
|
||||||
|
, select_reverse/2
|
||||||
|
, select_reverse/3
|
||||||
|
, select_reverse/1
|
||||||
|
, tab2list/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
|
||||||
-on_load(init/0).
|
-on_load(init/0).
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,7 +99,7 @@ open(#tab{name=_Name, named_table=_Named, type=Type, protection=Protection}=Tab,
|
||||||
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
||||||
Impl = impl_open(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions),
|
Impl = impl_open(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions),
|
||||||
%% @TODO implement named Impl (of sorts)
|
%% @TODO implement named Impl (of sorts)
|
||||||
Tab#tab{nif=Impl}.
|
Tab#tab{impl=Impl}.
|
||||||
|
|
||||||
destroy(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOptions) ->
|
destroy(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOptions) ->
|
||||||
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
||||||
|
@ -79,40 +109,16 @@ repair(#tab{type=Type, protection=Protection}, Options, ReadOptions, WriteOption
|
||||||
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
{value, {path,Path}, NewOptions} = lists:keytake(path, 1, Options),
|
||||||
impl_repair(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions).
|
impl_repair(Type, Protection, Path, NewOptions, ReadOptions, WriteOptions).
|
||||||
|
|
||||||
insert(#tab{keypos=KeyPos, type=Type}, Impl, Object) when is_tuple(Object) ->
|
delete(#tab{impl=Impl}) ->
|
||||||
Key = element(KeyPos,Object),
|
|
||||||
Val = Object,
|
|
||||||
impl_insert(Impl, encode(Type, Key), encode(Type, Val));
|
|
||||||
insert(#tab{keypos=KeyPos, type=Type}, Impl, Objects) when is_list(Objects) ->
|
|
||||||
List = [{encode(Type, element(KeyPos,Object)), encode(Type, Object)} || Object <- Objects ],
|
|
||||||
impl_insert(Impl, List).
|
|
||||||
|
|
||||||
insert_new(#tab{keypos=KeyPos, type=Type}, Impl, Object) when is_tuple(Object) ->
|
|
||||||
Key = element(KeyPos,Object),
|
|
||||||
Val = Object,
|
|
||||||
impl_insert_new(Impl, encode(Type, Key), encode(Type, Val));
|
|
||||||
insert_new(#tab{keypos=KeyPos, type=Type}, Impl, Objects) when is_list(Objects) ->
|
|
||||||
List = [{encode(Type, element(KeyPos,Object)), encode(Type, Object)} || Object <- Objects ],
|
|
||||||
impl_insert_new(Impl, List).
|
|
||||||
|
|
||||||
delete(_Tab, Impl) ->
|
|
||||||
impl_delete(Impl).
|
impl_delete(Impl).
|
||||||
|
|
||||||
delete(#tab{type=Type}, Impl, Key) ->
|
delete(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
impl_delete(Impl, encode(Type, Key)).
|
impl_delete(Impl, encode(Type, Key)).
|
||||||
|
|
||||||
delete_all_objects(_Tab, Impl) ->
|
delete_all_objects(#tab{impl=Impl}) ->
|
||||||
impl_delete_all_objects(Impl).
|
impl_delete_all_objects(Impl).
|
||||||
|
|
||||||
lookup(#tab{type=Type}, Impl, Key) ->
|
first(#tab{type=Type, impl=Impl}) ->
|
||||||
case impl_lookup(Impl, encode(Type, Key)) of
|
|
||||||
'$end_of_table' ->
|
|
||||||
[];
|
|
||||||
Object when is_binary(Object) ->
|
|
||||||
[decode(Type, Object)]
|
|
||||||
end.
|
|
||||||
|
|
||||||
first(#tab{type=Type}, Impl) ->
|
|
||||||
case impl_first(Impl) of
|
case impl_first(Impl) of
|
||||||
'$end_of_table' ->
|
'$end_of_table' ->
|
||||||
'$end_of_table';
|
'$end_of_table';
|
||||||
|
@ -120,15 +126,31 @@ first(#tab{type=Type}, Impl) ->
|
||||||
decode(Type, Key)
|
decode(Type, Key)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
next(#tab{type=Type}, Impl, Key) ->
|
first_iter(#tab{type=Type, impl=Impl}) ->
|
||||||
case impl_next(Impl, encode(Type, Key)) of
|
case impl_first_iter(Impl) of
|
||||||
'$end_of_table' ->
|
'$end_of_table' ->
|
||||||
'$end_of_table';
|
'$end_of_table';
|
||||||
Next ->
|
Key ->
|
||||||
decode(Type, Next)
|
decode(Type, Key)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
info_memory(_Tab, Impl) ->
|
last(#tab{type=Type, impl=Impl}) ->
|
||||||
|
case impl_last(Impl) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Key ->
|
||||||
|
decode(Type, Key)
|
||||||
|
end.
|
||||||
|
|
||||||
|
last_iter(#tab{type=Type, impl=Impl}) ->
|
||||||
|
case impl_last_iter(Impl) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Key ->
|
||||||
|
decode(Type, Key)
|
||||||
|
end.
|
||||||
|
|
||||||
|
info_memory(#tab{impl=Impl}) ->
|
||||||
case impl_info_memory(Impl) of
|
case impl_info_memory(Impl) of
|
||||||
Memory when is_integer(Memory) ->
|
Memory when is_integer(Memory) ->
|
||||||
erlang:round(Memory / erlang:system_info(wordsize));
|
erlang:round(Memory / erlang:system_info(wordsize));
|
||||||
|
@ -136,30 +158,286 @@ info_memory(_Tab, Impl) ->
|
||||||
Else
|
Else
|
||||||
end.
|
end.
|
||||||
|
|
||||||
info_size(_Tab, Impl) ->
|
info_size(#tab{impl=Impl}) ->
|
||||||
impl_info_size(Impl).
|
impl_info_size(Impl).
|
||||||
|
|
||||||
tab2list(Tab, Impl) ->
|
insert(#tab{keypos=KeyPos, type=Type, impl=Impl}, Object) when is_tuple(Object) ->
|
||||||
tab2list(Tab, Impl, impl_first(Impl), []).
|
Key = element(KeyPos, Object),
|
||||||
|
Val = Object,
|
||||||
|
impl_insert(Impl, encode(Type, Key), encode(Type, Val));
|
||||||
|
insert(#tab{keypos=KeyPos, type=Type, impl=Impl}, Objects) when is_list(Objects) ->
|
||||||
|
List = [{encode(Type, element(KeyPos, Object)), encode(Type, Object)} || Object <- Objects ],
|
||||||
|
impl_insert(Impl, List).
|
||||||
|
|
||||||
tab2list(_Tab, _Impl, '$end_of_table', Acc) ->
|
insert_new(#tab{keypos=KeyPos, type=Type, impl=Impl}, Object) when is_tuple(Object) ->
|
||||||
lists:reverse(Acc);
|
Key = element(KeyPos, Object),
|
||||||
tab2list(#tab{type=Type}=Tab, Impl, Key, Acc) ->
|
Val = Object,
|
||||||
NewAcc =
|
impl_insert_new(Impl, encode(Type, Key), encode(Type, Val));
|
||||||
case impl_lookup(Impl, Key) of
|
insert_new(#tab{keypos=KeyPos, type=Type, impl=Impl}, Objects) when is_list(Objects) ->
|
||||||
|
List = [{encode(Type, element(KeyPos, Object)), encode(Type, Object)} || Object <- Objects ],
|
||||||
|
impl_insert_new(Impl, List).
|
||||||
|
|
||||||
|
lookup(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_lookup(Impl, encode(Type, Key)) of
|
||||||
'$end_of_table' ->
|
'$end_of_table' ->
|
||||||
%% @NOTE This is not an atomic operation
|
[];
|
||||||
Acc;
|
|
||||||
Object when is_binary(Object) ->
|
Object when is_binary(Object) ->
|
||||||
[decode(Type, Object)|Acc]
|
[decode(Type, Object)]
|
||||||
|
end.
|
||||||
|
|
||||||
|
lookup_element(#tab{type=Type, impl=Impl}, Key, Pos) ->
|
||||||
|
Element =
|
||||||
|
case impl_lookup(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Object when is_binary(Object) ->
|
||||||
|
decode(Type, Object)
|
||||||
end,
|
end,
|
||||||
tab2list(Tab, Impl, impl_next(Impl, Key), NewAcc).
|
element(Pos, Element).
|
||||||
|
|
||||||
|
member(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
impl_member(Impl, encode(Type, Key)).
|
||||||
|
|
||||||
|
next(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_next(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Next ->
|
||||||
|
decode(Type, Next)
|
||||||
|
end.
|
||||||
|
|
||||||
|
next_iter(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_next_iter(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Next ->
|
||||||
|
decode(Type, Next)
|
||||||
|
end.
|
||||||
|
|
||||||
|
prev(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_prev(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Prev ->
|
||||||
|
decode(Type, Prev)
|
||||||
|
end.
|
||||||
|
|
||||||
|
prev_iter(#tab{type=Type, impl=Impl}, Key) ->
|
||||||
|
case impl_prev_iter(Impl, encode(Type, Key)) of
|
||||||
|
'$end_of_table' ->
|
||||||
|
'$end_of_table';
|
||||||
|
Prev ->
|
||||||
|
decode(Type, Prev)
|
||||||
|
end.
|
||||||
|
|
||||||
|
foldl(Fun, Acc0, Tab) ->
|
||||||
|
foldl(Fun, Acc0, Tab, first_iter(Tab)).
|
||||||
|
|
||||||
|
foldr(Fun, Acc0, Tab) ->
|
||||||
|
foldr(Fun, Acc0, Tab, last_iter(Tab)).
|
||||||
|
|
||||||
|
nfoldl(Fun, Acc0, Tab, Limit) when Limit > 0 ->
|
||||||
|
nfoldl(Fun, Acc0, Acc0, Tab, Limit, Limit, first_iter(Tab));
|
||||||
|
nfoldl(_Fun, _Acc0, _Tab, Limit) ->
|
||||||
|
exit({badarg,Limit}).
|
||||||
|
|
||||||
|
nfoldl('$end_of_table') ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldl({_Fun, _Acc0, _Tab, _Limit0, '$end_of_table'}) ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldl({Fun, Acc0, Tab, Limit0, Key}) ->
|
||||||
|
nfoldl(Fun, Acc0, Acc0, Tab, Limit0, Limit0, next_iter(Tab, Key)).
|
||||||
|
|
||||||
|
nfoldr(Fun, Acc0, Tab, Limit) when Limit > 0 ->
|
||||||
|
nfoldr(Fun, Acc0, Acc0, Tab, Limit, Limit, last_iter(Tab));
|
||||||
|
nfoldr(_Fun, _Acc0, _Tab, Limit) ->
|
||||||
|
exit({badarg,Limit}).
|
||||||
|
|
||||||
|
nfoldr('$end_of_table') ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldr({_Fun, _Acc0, _Tab, _Limit0, '$end_of_table'}) ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldr({Fun, Acc0, Tab, Limit0, Key}) ->
|
||||||
|
nfoldr(Fun, Acc0, Acc0, Tab, Limit0, Limit0, prev_iter(Tab, Key)).
|
||||||
|
|
||||||
|
tab2list(Tab) ->
|
||||||
|
foldr(fun(X, Acc) -> [X|Acc] end, [], Tab).
|
||||||
|
|
||||||
|
match(Tab, Pattern) ->
|
||||||
|
select(Tab, [{Pattern, [], ['$$']}]).
|
||||||
|
|
||||||
|
match(Tab, Pattern, Limit) ->
|
||||||
|
select(Tab, [{Pattern, [], ['$$']}], Limit).
|
||||||
|
|
||||||
|
match(Cont) ->
|
||||||
|
select(Cont).
|
||||||
|
|
||||||
|
match_delete(Tab, Pattern) ->
|
||||||
|
select_delete(Tab, [{Pattern, [], [true]}]),
|
||||||
|
true.
|
||||||
|
|
||||||
|
match_object(Tab, Pattern) ->
|
||||||
|
select(Tab, [{Pattern, [], ['$_']}]).
|
||||||
|
|
||||||
|
match_object(Tab, Pattern, Limit) ->
|
||||||
|
select(Tab, [{Pattern, [], ['$_']}], Limit).
|
||||||
|
|
||||||
|
match_object(Cont) ->
|
||||||
|
select(Cont).
|
||||||
|
|
||||||
|
select(Tab, Spec) ->
|
||||||
|
Fun = fun(_Object, Match, Acc) -> [Match|Acc] end,
|
||||||
|
selectr(Fun, [], Tab, Spec).
|
||||||
|
|
||||||
|
select(Tab, Spec, Limit) ->
|
||||||
|
Fun = fun(_Object, Match, Acc) -> [Match|Acc] end,
|
||||||
|
case nselectl(Fun, [], Tab, Spec, Limit) of
|
||||||
|
{Acc, Cont} ->
|
||||||
|
{lists:reverse(Acc), Cont};
|
||||||
|
Cont ->
|
||||||
|
Cont
|
||||||
|
end.
|
||||||
|
|
||||||
|
select(Cont0) ->
|
||||||
|
case nselectl(Cont0) of
|
||||||
|
{Acc, Cont} ->
|
||||||
|
{lists:reverse(Acc), Cont};
|
||||||
|
Cont ->
|
||||||
|
Cont
|
||||||
|
end.
|
||||||
|
|
||||||
|
select_count(Tab, Spec) ->
|
||||||
|
Fun = fun(_Object, true, Acc) ->
|
||||||
|
Acc + 1;
|
||||||
|
(_Object, _Match, Acc) ->
|
||||||
|
Acc
|
||||||
|
end,
|
||||||
|
selectl(Fun, 0, Tab, Spec).
|
||||||
|
|
||||||
|
select_delete(#tab{keypos=KeyPos}=Tab, Spec) ->
|
||||||
|
Fun = fun(Object, true, Acc) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
delete(Tab, Key),
|
||||||
|
Acc + 1;
|
||||||
|
(_Object, _Match, Acc) ->
|
||||||
|
Acc
|
||||||
|
end,
|
||||||
|
selectl(Fun, 0, Tab, Spec).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec) ->
|
||||||
|
Fun = fun(_Object, Match, Acc) -> [Match|Acc] end,
|
||||||
|
selectl(Fun, [], Tab, Spec).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec, Limit) ->
|
||||||
|
Fun = fun(_Object, Match, Acc) -> [Match|Acc] end,
|
||||||
|
case nselectr(Fun, [], Tab, Spec, Limit) of
|
||||||
|
{Acc, Cont} ->
|
||||||
|
{lists:reverse(Acc), Cont};
|
||||||
|
Cont ->
|
||||||
|
Cont
|
||||||
|
end.
|
||||||
|
|
||||||
|
select_reverse(Cont0) ->
|
||||||
|
case nselectr(Cont0) of
|
||||||
|
{Acc, Cont} ->
|
||||||
|
{lists:reverse(Acc), Cont};
|
||||||
|
Cont ->
|
||||||
|
Cont
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
foldl(_Fun, Acc, _Tab, '$end_of_table') ->
|
||||||
|
Acc;
|
||||||
|
foldl(Fun, Acc, #tab{keypos=KeyPos}=Tab, Object) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
foldl(Fun, Fun(Object, Acc), Tab, next_iter(Tab, Key)).
|
||||||
|
|
||||||
|
foldr(_Fun, Acc, _Tab, '$end_of_table') ->
|
||||||
|
Acc;
|
||||||
|
foldr(Fun, Acc, #tab{keypos=KeyPos}=Tab, Object) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
foldr(Fun, Fun(Object, Acc), Tab, prev_iter(Tab, Key)).
|
||||||
|
|
||||||
|
nfoldl(_Fun, Acc0, Acc0, _Tab, _Limit0, _Limit, '$end_of_table') ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldl(_Fun, _Acc0, Acc, _Tab, _Limit0, _Limit, '$end_of_table'=Cont) ->
|
||||||
|
{Acc, Cont};
|
||||||
|
nfoldl(Fun, Acc0, Acc, #tab{keypos=KeyPos}=Tab, Limit0, Limit, Object) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
case Fun(Object, Acc) of
|
||||||
|
{true, NewAcc} ->
|
||||||
|
if Limit > 1 ->
|
||||||
|
nfoldl(Fun, Acc0, NewAcc, Tab, Limit0, Limit-1, next_iter(Tab, Key));
|
||||||
|
true ->
|
||||||
|
Cont = {Fun, Acc0, Tab, Limit0, Key},
|
||||||
|
{NewAcc, Cont}
|
||||||
|
end;
|
||||||
|
{false, NewAcc} ->
|
||||||
|
nfoldl(Fun, Acc0, NewAcc, Tab, Limit0, Limit, next_iter(Tab, Key))
|
||||||
|
end.
|
||||||
|
|
||||||
|
nfoldr(_Fun, Acc0, Acc0, _Tab, _Limit0, _Limit, '$end_of_table') ->
|
||||||
|
'$end_of_table';
|
||||||
|
nfoldr(_Fun, _Acc0, Acc, _Tab, _Limit0, _Limit, '$end_of_table'=Cont) ->
|
||||||
|
{Acc, Cont};
|
||||||
|
nfoldr(Fun, Acc0, Acc, #tab{keypos=KeyPos}=Tab, Limit0, Limit, Object) ->
|
||||||
|
Key = element(KeyPos, Object),
|
||||||
|
case Fun(Object, Acc) of
|
||||||
|
{true, NewAcc} ->
|
||||||
|
if Limit > 1 ->
|
||||||
|
nfoldr(Fun, Acc0, NewAcc, Tab, Limit0, Limit-1, prev_iter(Tab, Key));
|
||||||
|
true ->
|
||||||
|
Cont = {Fun, Acc0, Tab, Limit0, Key},
|
||||||
|
{NewAcc, Cont}
|
||||||
|
end;
|
||||||
|
{false, NewAcc} ->
|
||||||
|
nfoldr(Fun, Acc0, NewAcc, Tab, Limit0, Limit, prev_iter(Tab, Key))
|
||||||
|
end.
|
||||||
|
|
||||||
|
selectl(Fun, Acc0, Tab, Spec) ->
|
||||||
|
foldl(selectfun(Fun, Spec), Acc0, Tab).
|
||||||
|
|
||||||
|
selectr(Fun, Acc0, Tab, Spec) ->
|
||||||
|
foldr(selectfun(Fun, Spec), Acc0, Tab).
|
||||||
|
|
||||||
|
nselectl(Fun, Acc0, Tab, Spec, Limit0) ->
|
||||||
|
nfoldl(nselectfun(Fun, Spec), Acc0, Tab, Limit0).
|
||||||
|
|
||||||
|
nselectr(Fun, Acc0, Tab, Spec, Limit0) ->
|
||||||
|
nfoldr(nselectfun(Fun, Spec), Acc0, Tab, Limit0).
|
||||||
|
|
||||||
|
nselectl(Cont) ->
|
||||||
|
nfoldl(Cont).
|
||||||
|
|
||||||
|
nselectr(Cont) ->
|
||||||
|
nfoldr(Cont).
|
||||||
|
|
||||||
|
selectfun(Fun, Spec) ->
|
||||||
|
CMSpec = ets:match_spec_compile(Spec),
|
||||||
|
fun(Object, Acc) ->
|
||||||
|
case ets:match_spec_run([Object], CMSpec) of
|
||||||
|
[] ->
|
||||||
|
Acc;
|
||||||
|
[Match] ->
|
||||||
|
Fun(Object, Match, Acc)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
nselectfun(Fun, Spec) ->
|
||||||
|
CMSpec = ets:match_spec_compile(Spec),
|
||||||
|
fun(Object, Acc) ->
|
||||||
|
case ets:match_spec_run([Object], CMSpec) of
|
||||||
|
[] ->
|
||||||
|
{false, Acc};
|
||||||
|
[Match] ->
|
||||||
|
{true, Fun(Object, Match, Acc)}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
encode(set, Term) ->
|
encode(set, Term) ->
|
||||||
term_to_binary(Term);
|
term_to_binary(Term);
|
||||||
encode(ordered_set, Term) ->
|
encode(ordered_set, Term) ->
|
||||||
|
@ -182,6 +460,33 @@ impl_destroy(_Type, _Protection, _Path, _Options, _ReadOptions, _WriteOptions) -
|
||||||
impl_repair(_Type, _Protection, _Path, _Options, _ReadOptions, _WriteOptions) ->
|
impl_repair(_Type, _Protection, _Path, _Options, _ReadOptions, _WriteOptions) ->
|
||||||
?NIF_STUB.
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_delete(_Impl) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_delete(_Impl, _Key) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_delete_all_objects(_Impl) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_first(_Impl) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_first_iter(_Impl) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_last(_Impl) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_last_iter(_Impl) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_info_memory(_Impl) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_info_size(_Impl) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
impl_insert(_Impl, _Key, _Object) ->
|
impl_insert(_Impl, _Key, _Object) ->
|
||||||
?NIF_STUB.
|
?NIF_STUB.
|
||||||
|
|
||||||
|
@ -194,26 +499,20 @@ impl_insert_new(_Impl, _Key, _Object) ->
|
||||||
impl_insert_new(_Impl, _List) ->
|
impl_insert_new(_Impl, _List) ->
|
||||||
?NIF_STUB.
|
?NIF_STUB.
|
||||||
|
|
||||||
impl_delete(_Impl) ->
|
|
||||||
?NIF_STUB.
|
|
||||||
|
|
||||||
impl_delete(_Impl, _Key) ->
|
|
||||||
?NIF_STUB.
|
|
||||||
|
|
||||||
impl_delete_all_objects(_Impl) ->
|
|
||||||
?NIF_STUB.
|
|
||||||
|
|
||||||
impl_lookup(_Impl, _Key) ->
|
impl_lookup(_Impl, _Key) ->
|
||||||
?NIF_STUB.
|
?NIF_STUB.
|
||||||
|
|
||||||
impl_first(_Impl) ->
|
impl_member(_Impl, _Key) ->
|
||||||
?NIF_STUB.
|
?NIF_STUB.
|
||||||
|
|
||||||
impl_next(_Impl, _Key) ->
|
impl_next(_Impl, _Key) ->
|
||||||
?NIF_STUB.
|
?NIF_STUB.
|
||||||
|
|
||||||
impl_info_memory(_Impl) ->
|
impl_next_iter(_Impl, _Key) ->
|
||||||
?NIF_STUB.
|
?NIF_STUB.
|
||||||
|
|
||||||
impl_info_size(_Impl) ->
|
impl_prev(_Impl, _Key) ->
|
||||||
|
?NIF_STUB.
|
||||||
|
|
||||||
|
impl_prev_iter(_Impl, _Key) ->
|
||||||
?NIF_STUB.
|
?NIF_STUB.
|
||||||
|
|
|
@ -33,15 +33,37 @@
|
||||||
, new/3
|
, new/3
|
||||||
, destroy/3
|
, destroy/3
|
||||||
, repair/3
|
, repair/3
|
||||||
, insert/2
|
|
||||||
, insert_new/2
|
|
||||||
, delete/1
|
, delete/1
|
||||||
, delete/2
|
, delete/2
|
||||||
, delete_all_objects/1
|
, delete_all_objects/1
|
||||||
, lookup/2
|
|
||||||
, first/1
|
, first/1
|
||||||
, next/2
|
, foldl/3
|
||||||
|
, foldr/3
|
||||||
|
, info/1
|
||||||
, info/2
|
, info/2
|
||||||
|
, insert/2
|
||||||
|
, insert_new/2
|
||||||
|
, last/1
|
||||||
|
, lookup/2
|
||||||
|
, lookup_element/3
|
||||||
|
, match/2
|
||||||
|
, match/3
|
||||||
|
, match/1
|
||||||
|
, match_delete/2
|
||||||
|
, match_object/2
|
||||||
|
, match_object/3
|
||||||
|
, match_object/1
|
||||||
|
, member/2
|
||||||
|
, next/2
|
||||||
|
, prev/2
|
||||||
|
, select/2
|
||||||
|
, select/3
|
||||||
|
, select/1
|
||||||
|
, select_count/2
|
||||||
|
, select_delete/2
|
||||||
|
, select_reverse/2
|
||||||
|
, select_reverse/3
|
||||||
|
, select_reverse/1
|
||||||
, tab2list/1
|
, tab2list/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
@ -49,7 +71,7 @@
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
-record(state, {tab}).
|
-record(state, {tab, name, conts=dict:new()}).
|
||||||
|
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
@ -76,12 +98,6 @@ destroy(Tab, Name, Options) ->
|
||||||
repair(Tab, Name, Options) ->
|
repair(Tab, Name, Options) ->
|
||||||
qc_lets_raw:repair(Tab, Name, Options).
|
qc_lets_raw:repair(Tab, Name, Options).
|
||||||
|
|
||||||
insert(Tab, ObjOrObjs) ->
|
|
||||||
gen_server:call(Tab, {insert, ObjOrObjs}).
|
|
||||||
|
|
||||||
insert_new(Tab, ObjOrObjs) ->
|
|
||||||
gen_server:call(Tab, {insert_new, ObjOrObjs}).
|
|
||||||
|
|
||||||
delete(Tab) ->
|
delete(Tab) ->
|
||||||
gen_server:call(Tab, delete).
|
gen_server:call(Tab, delete).
|
||||||
|
|
||||||
|
@ -91,17 +107,101 @@ delete(Tab, Key) ->
|
||||||
delete_all_objects(Tab) ->
|
delete_all_objects(Tab) ->
|
||||||
gen_server:call(Tab, delete_all_objects).
|
gen_server:call(Tab, delete_all_objects).
|
||||||
|
|
||||||
|
first(Tab) ->
|
||||||
|
gen_server:call(Tab, first).
|
||||||
|
|
||||||
|
foldl(Function, Acc0, Tab) ->
|
||||||
|
gen_server:call(Tab, {foldl, Function, Acc0}).
|
||||||
|
|
||||||
|
foldr(Function, Acc0, Tab) ->
|
||||||
|
gen_server:call(Tab, {foldr, Function, Acc0}).
|
||||||
|
|
||||||
|
info(Tab) ->
|
||||||
|
gen_server:call(Tab, info).
|
||||||
|
|
||||||
|
info(Tab, Item) ->
|
||||||
|
gen_server:call(Tab, {info, Item}).
|
||||||
|
|
||||||
|
insert(Tab, ObjOrObjs) ->
|
||||||
|
gen_server:call(Tab, {insert, ObjOrObjs}).
|
||||||
|
|
||||||
|
insert_new(Tab, ObjOrObjs) ->
|
||||||
|
gen_server:call(Tab, {insert_new, ObjOrObjs}).
|
||||||
|
|
||||||
|
last(Tab) ->
|
||||||
|
gen_server:call(Tab, last).
|
||||||
|
|
||||||
lookup(Tab, Key) ->
|
lookup(Tab, Key) ->
|
||||||
gen_server:call(Tab, {lookup, Key}).
|
gen_server:call(Tab, {lookup, Key}).
|
||||||
|
|
||||||
first(Tab) ->
|
lookup_element(Tab, Key, Pos) ->
|
||||||
gen_server:call(Tab, first).
|
gen_server:call(Tab, {lookup_element, Key, Pos}).
|
||||||
|
|
||||||
|
match(Tab, Pattern) ->
|
||||||
|
gen_server:call(Tab, {match, Pattern}).
|
||||||
|
|
||||||
|
match(Tab, Pattern, Limit) ->
|
||||||
|
gen_server:call(Tab, {match, Pattern, Limit}).
|
||||||
|
|
||||||
|
match({wcont, _, _}=Cont0) ->
|
||||||
|
{Tab, Cont} = unwrap_cont(Cont0),
|
||||||
|
gen_server:call(Tab, {match_cont, Cont});
|
||||||
|
match('$end_of_table') ->
|
||||||
|
'$end_of_table'.
|
||||||
|
|
||||||
|
match_delete(Tab, Pattern) ->
|
||||||
|
gen_server:call(Tab, {match_delete, Pattern}).
|
||||||
|
|
||||||
|
match_object(Tab, Pattern) ->
|
||||||
|
gen_server:call(Tab, {match_object, Pattern}).
|
||||||
|
|
||||||
|
match_object(Tab, Pattern, Limit) ->
|
||||||
|
gen_server:call(Tab, {match_object, Pattern, Limit}).
|
||||||
|
|
||||||
|
match_object({wcont, _, _}=Cont0) ->
|
||||||
|
{Tab, Cont} = unwrap_cont(Cont0),
|
||||||
|
gen_server:call(Tab, {match_object_cont, Cont});
|
||||||
|
match_object('$end_of_table') ->
|
||||||
|
'$end_of_table'.
|
||||||
|
|
||||||
|
member(Tab, Key) ->
|
||||||
|
gen_server:call(Tab, {member, Key}).
|
||||||
|
|
||||||
next(Tab, Key) ->
|
next(Tab, Key) ->
|
||||||
gen_server:call(Tab, {next, Key}).
|
gen_server:call(Tab, {next, Key}).
|
||||||
|
|
||||||
info(Tab, Item) ->
|
prev(Tab, Key) ->
|
||||||
gen_server:call(Tab, {info, Item}).
|
gen_server:call(Tab, {prev, Key}).
|
||||||
|
|
||||||
|
select(Tab, Spec) ->
|
||||||
|
gen_server:call(Tab, {select, Spec}).
|
||||||
|
|
||||||
|
select(Tab, Spec, Limit) ->
|
||||||
|
gen_server:call(Tab, {select, Spec, Limit}).
|
||||||
|
|
||||||
|
select({wcont, _, _}=Cont0) ->
|
||||||
|
{Tab, Cont} = unwrap_cont(Cont0),
|
||||||
|
gen_server:call(Tab, {select_cont, Cont});
|
||||||
|
select('$end_of_table') ->
|
||||||
|
'$end_of_table'.
|
||||||
|
|
||||||
|
select_count(Tab, Spec) ->
|
||||||
|
gen_server:call(Tab, {select_count, Spec}).
|
||||||
|
|
||||||
|
select_delete(Tab, Spec) ->
|
||||||
|
gen_server:call(Tab, {select_delete, Spec}).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec) ->
|
||||||
|
gen_server:call(Tab, {select_reverse, Spec}).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec, Limit) ->
|
||||||
|
gen_server:call(Tab, {select_reverse, Spec, Limit}).
|
||||||
|
|
||||||
|
select_reverse({wcont, _, _}=Cont0) ->
|
||||||
|
{Tab, Cont} = unwrap_cont(Cont0),
|
||||||
|
gen_server:call(Tab, {select_reverse_cont, Cont});
|
||||||
|
select_reverse('$end_of_table') ->
|
||||||
|
'$end_of_table'.
|
||||||
|
|
||||||
tab2list(Tab) ->
|
tab2list(Tab) ->
|
||||||
gen_server:call(Tab, tab2list).
|
gen_server:call(Tab, tab2list).
|
||||||
|
@ -117,19 +217,13 @@ tab2list(Tab) ->
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
init([Name, Options]) ->
|
init([Name, Options]) ->
|
||||||
Tab = qc_lets_raw:new(Name, Options),
|
Tab = qc_lets_raw:new(Name, Options),
|
||||||
{ok, #state{tab=Tab}}.
|
{ok, #state{tab=Tab, name=Name}}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% @private
|
%% @private
|
||||||
%% @doc
|
%% @doc
|
||||||
%% Handling call messages
|
%% Handling call messages
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
handle_call({insert, ObjOrObjs}, _From, #state{tab=Tab}=State) ->
|
|
||||||
Reply = qc_lets_raw:insert(Tab, ObjOrObjs),
|
|
||||||
{reply, Reply, State};
|
|
||||||
handle_call({insert_new, ObjOrObjs}, _From, #state{tab=Tab}=State) ->
|
|
||||||
Reply = qc_lets_raw:insert_new(Tab, ObjOrObjs),
|
|
||||||
{reply, Reply, State};
|
|
||||||
handle_call(delete, _From, #state{tab=Tab}=State) ->
|
handle_call(delete, _From, #state{tab=Tab}=State) ->
|
||||||
Reply = qc_lets_raw:delete(Tab),
|
Reply = qc_lets_raw:delete(Tab),
|
||||||
{stop, normal, Reply, State#state{tab=undefined}};
|
{stop, normal, Reply, State#state{tab=undefined}};
|
||||||
|
@ -139,18 +233,94 @@ handle_call({delete, Key}, _From, #state{tab=Tab}=State) ->
|
||||||
handle_call(delete_all_objects, _From, #state{tab=Tab}=State) ->
|
handle_call(delete_all_objects, _From, #state{tab=Tab}=State) ->
|
||||||
Reply = qc_lets_raw:delete_all_objects(Tab),
|
Reply = qc_lets_raw:delete_all_objects(Tab),
|
||||||
{reply, Reply, State};
|
{reply, Reply, State};
|
||||||
handle_call({lookup, Key}, _From, #state{tab=Tab}=State) ->
|
|
||||||
Reply = qc_lets_raw:lookup(Tab, Key),
|
|
||||||
{reply, Reply, State};
|
|
||||||
handle_call(first, _From, #state{tab=Tab}=State) ->
|
handle_call(first, _From, #state{tab=Tab}=State) ->
|
||||||
Reply = qc_lets_raw:first(Tab),
|
Reply = qc_lets_raw:first(Tab),
|
||||||
{reply, Reply, State};
|
{reply, Reply, State};
|
||||||
handle_call({next, Key}, _From, #state{tab=Tab}=State) ->
|
handle_call({foldl, Function, Acc0}, _From, #state{tab=Tab}=State) ->
|
||||||
Reply = qc_lets_raw:next(Tab, Key),
|
Reply = qc_lets_raw:foldl(Function, Acc0, Tab),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({foldr, Function, Acc0}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:foldr(Function, Acc0, Tab),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call(info, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:info(Tab),
|
||||||
{reply, Reply, State};
|
{reply, Reply, State};
|
||||||
handle_call({info, Item}, _From, #state{tab=Tab}=State) ->
|
handle_call({info, Item}, _From, #state{tab=Tab}=State) ->
|
||||||
Reply = qc_lets_raw:info(Tab, Item),
|
Reply = qc_lets_raw:info(Tab, Item),
|
||||||
{reply, Reply, State};
|
{reply, Reply, State};
|
||||||
|
handle_call({insert, ObjOrObjs}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:insert(Tab, ObjOrObjs),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({insert_new, ObjOrObjs}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:insert_new(Tab, ObjOrObjs),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call(last, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:last(Tab),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({lookup, Key}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:lookup(Tab, Key),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({lookup_element, Key, Pos}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:lookup_element(Tab, Key, Pos),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({match, Pattern}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:match(Tab, Pattern),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({match, Pattern, Limit}, _From, #state{tab=Tab}=State) ->
|
||||||
|
{Reply, NewState} = wrap_cont(qc_lets_raw:match(Tab, Pattern, Limit), State),
|
||||||
|
{reply, Reply, NewState};
|
||||||
|
handle_call({match_cont, Cont0}, _From, State0) ->
|
||||||
|
{Cont, State} = unwrap_cont(Cont0, State0),
|
||||||
|
{Reply, NewState} = wrap_cont(qc_lets_raw:match(Cont), State),
|
||||||
|
{reply, Reply, NewState};
|
||||||
|
handle_call({match_delete, Pattern}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:match_delete(Tab, Pattern),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({match_object, Pattern}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:match_object(Tab, Pattern),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({match_object, Pattern, Limit}, _From, #state{tab=Tab}=State) ->
|
||||||
|
{Reply, NewState} = wrap_cont(qc_lets_raw:match_object(Tab, Pattern, Limit), State),
|
||||||
|
{reply, Reply, NewState};
|
||||||
|
handle_call({match_object_cont, Cont0}, _From, State0) ->
|
||||||
|
{Cont, State} = unwrap_cont(Cont0, State0),
|
||||||
|
{Reply, NewState} = wrap_cont(qc_lets_raw:match_object(Cont), State),
|
||||||
|
{reply, Reply, NewState};
|
||||||
|
handle_call({member, Key}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:member(Tab, Key),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({next, Key}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:next(Tab, Key),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({prev, Key}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:prev(Tab, Key),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({select_count, Spec}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:select_count(Tab, Spec),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({select_delete, Spec}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:select_delete(Tab, Spec),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({select, Spec}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:select(Tab, Spec),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({select, Spec, Limit}, _From, #state{tab=Tab}=State) ->
|
||||||
|
{Reply, NewState} = wrap_cont(qc_lets_raw:select(Tab, Spec, Limit), State),
|
||||||
|
{reply, Reply, NewState};
|
||||||
|
handle_call({select_cont, Cont0}, _From, State0) ->
|
||||||
|
{Cont, State} = unwrap_cont(Cont0, State0),
|
||||||
|
{Reply, NewState} = wrap_cont(qc_lets_raw:select(Cont), State),
|
||||||
|
{reply, Reply, NewState};
|
||||||
|
handle_call({select_reverse, Spec}, _From, #state{tab=Tab}=State) ->
|
||||||
|
Reply = qc_lets_raw:select_reverse(Tab, Spec),
|
||||||
|
{reply, Reply, State};
|
||||||
|
handle_call({select_reverse, Spec, Limit}, _From, #state{tab=Tab}=State) ->
|
||||||
|
{Reply, NewState} = wrap_cont(qc_lets_raw:select_reverse(Tab, Spec, Limit), State),
|
||||||
|
{reply, Reply, NewState};
|
||||||
|
handle_call({select_reverse_cont, Cont0}, _From, State0) ->
|
||||||
|
{Cont, State} = unwrap_cont(Cont0, State0),
|
||||||
|
{Reply, NewState} = wrap_cont(qc_lets_raw:select_reverse(Cont), State),
|
||||||
|
{reply, Reply, NewState};
|
||||||
handle_call(tab2list, _From, #state{tab=Tab}=State) ->
|
handle_call(tab2list, _From, #state{tab=Tab}=State) ->
|
||||||
Reply = qc_lets_raw:tab2list(Tab),
|
Reply = qc_lets_raw:tab2list(Tab),
|
||||||
{reply, Reply, State}.
|
{reply, Reply, State}.
|
||||||
|
@ -193,5 +363,28 @@ code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
|
wrap_cont({'EXIT', _}=Err, State) ->
|
||||||
|
{Err, State};
|
||||||
|
wrap_cont('$end_of_table'=Reply, State) ->
|
||||||
|
{Reply, State};
|
||||||
|
wrap_cont({_Match, '$end_of_table'}=Reply, State) ->
|
||||||
|
{Reply, State};
|
||||||
|
wrap_cont({_Match, Cont}=Reply, #state{name=Name, conts=Conts}=State) ->
|
||||||
|
ServerRef = {Name, node()},
|
||||||
|
Ref = make_ref(),
|
||||||
|
NewState = State#state{conts=dict:store(Ref, Cont, Conts)},
|
||||||
|
{setelement(2, Reply, wrap_cont({ServerRef, Ref})), NewState}.
|
||||||
|
|
||||||
|
unwrap_cont(Ref, #state{conts=Conts}=State) ->
|
||||||
|
Cont = dict:fetch(Ref, Conts),
|
||||||
|
NewState = State#state{conts=dict:erase(Ref, Conts)},
|
||||||
|
{Cont, NewState}.
|
||||||
|
|
||||||
|
wrap_cont({ServerRef, Cont}) ->
|
||||||
|
{wcont, ServerRef, Cont}.
|
||||||
|
|
||||||
|
unwrap_cont({wcont, ServerRef, Cont}) ->
|
||||||
|
{ServerRef, Cont}.
|
||||||
|
|
|
@ -33,15 +33,37 @@
|
||||||
, new/3
|
, new/3
|
||||||
, destroy/3
|
, destroy/3
|
||||||
, repair/3
|
, repair/3
|
||||||
, insert/2
|
|
||||||
, insert_new/2
|
|
||||||
, delete/1
|
, delete/1
|
||||||
, delete/2
|
, delete/2
|
||||||
, delete_all_objects/1
|
, delete_all_objects/1
|
||||||
, lookup/2
|
|
||||||
, first/1
|
, first/1
|
||||||
, next/2
|
, foldl/3
|
||||||
|
, foldr/3
|
||||||
|
, info/1
|
||||||
, info/2
|
, info/2
|
||||||
|
, insert/2
|
||||||
|
, insert_new/2
|
||||||
|
, last/1
|
||||||
|
, lookup/2
|
||||||
|
, lookup_element/3
|
||||||
|
, match/2
|
||||||
|
, match/3
|
||||||
|
, match/1
|
||||||
|
, match_delete/2
|
||||||
|
, match_object/2
|
||||||
|
, match_object/3
|
||||||
|
, match_object/1
|
||||||
|
, member/2
|
||||||
|
, next/2
|
||||||
|
, prev/2
|
||||||
|
, select/2
|
||||||
|
, select/3
|
||||||
|
, select/1
|
||||||
|
, select_count/2
|
||||||
|
, select_delete/2
|
||||||
|
, select_reverse/2
|
||||||
|
, select_reverse/3
|
||||||
|
, select_reverse/1
|
||||||
, tab2list/1
|
, tab2list/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
@ -75,12 +97,6 @@ repair(_Tab, Name, Options) ->
|
||||||
%% _Tab is to help control generators and shrinking
|
%% _Tab is to help control generators and shrinking
|
||||||
catch lets:repair(Name, filter_options(Options)).
|
catch lets:repair(Name, filter_options(Options)).
|
||||||
|
|
||||||
insert(Tab, ObjOrObjs) ->
|
|
||||||
catch lets:insert(Tab, ObjOrObjs).
|
|
||||||
|
|
||||||
insert_new(Tab, ObjOrObjs) ->
|
|
||||||
catch lets:insert_new(Tab, ObjOrObjs).
|
|
||||||
|
|
||||||
delete(Tab) ->
|
delete(Tab) ->
|
||||||
catch lets:delete(Tab).
|
catch lets:delete(Tab).
|
||||||
|
|
||||||
|
@ -90,17 +106,89 @@ delete(Tab, Key) ->
|
||||||
delete_all_objects(Tab) ->
|
delete_all_objects(Tab) ->
|
||||||
catch lets:delete_all_objects(Tab).
|
catch lets:delete_all_objects(Tab).
|
||||||
|
|
||||||
|
first(Tab) ->
|
||||||
|
catch lets:first(Tab).
|
||||||
|
|
||||||
|
foldl(Function, Acc0, Tab) ->
|
||||||
|
catch lets:foldl(Function, Acc0, Tab).
|
||||||
|
|
||||||
|
foldr(Function, Acc0, Tab) ->
|
||||||
|
catch lets:foldr(Function, Acc0, Tab).
|
||||||
|
|
||||||
|
info(Tab) ->
|
||||||
|
catch lets:info(Tab).
|
||||||
|
|
||||||
|
info(Tab, Item) ->
|
||||||
|
catch lets:info(Tab, Item).
|
||||||
|
|
||||||
|
insert(Tab, ObjOrObjs) ->
|
||||||
|
catch lets:insert(Tab, ObjOrObjs).
|
||||||
|
|
||||||
|
insert_new(Tab, ObjOrObjs) ->
|
||||||
|
catch lets:insert_new(Tab, ObjOrObjs).
|
||||||
|
|
||||||
|
last(Tab) ->
|
||||||
|
catch lets:last(Tab).
|
||||||
|
|
||||||
lookup(Tab, Key) ->
|
lookup(Tab, Key) ->
|
||||||
catch lets:lookup(Tab, Key).
|
catch lets:lookup(Tab, Key).
|
||||||
|
|
||||||
first(Tab) ->
|
lookup_element(Tab, Key, Pos) ->
|
||||||
catch lets:first(Tab).
|
catch lets:lookup_element(Tab, Key, Pos).
|
||||||
|
|
||||||
|
match(Tab, Pattern) ->
|
||||||
|
catch lets:match(Tab, Pattern).
|
||||||
|
|
||||||
|
match(Tab, Pattern, Limit) ->
|
||||||
|
catch lets:match(Tab, Pattern, Limit).
|
||||||
|
|
||||||
|
match(Cont) ->
|
||||||
|
catch lets:match(Cont).
|
||||||
|
|
||||||
|
match_delete(Tab, Pattern) ->
|
||||||
|
catch lets:match_delete(Tab, Pattern).
|
||||||
|
|
||||||
|
match_object(Tab, Pattern) ->
|
||||||
|
catch lets:match_object(Tab, Pattern).
|
||||||
|
|
||||||
|
match_object(Tab, Pattern, Limit) ->
|
||||||
|
catch lets:match_object(Tab, Pattern, Limit).
|
||||||
|
|
||||||
|
match_object(Cont) ->
|
||||||
|
catch lets:match_object(Cont).
|
||||||
|
|
||||||
|
member(Tab, Key) ->
|
||||||
|
catch lets:member(Tab, Key).
|
||||||
|
|
||||||
next(Tab, Key) ->
|
next(Tab, Key) ->
|
||||||
catch lets:next(Tab, Key).
|
catch lets:next(Tab, Key).
|
||||||
|
|
||||||
info(Tab, Item) ->
|
prev(Tab, Key) ->
|
||||||
catch lets:info(Tab, Item).
|
catch lets:prev(Tab, Key).
|
||||||
|
|
||||||
|
select(Tab, Spec) ->
|
||||||
|
catch lets:select(Tab, Spec).
|
||||||
|
|
||||||
|
select(Tab, Spec, Limit) ->
|
||||||
|
catch lets:select(Tab, Spec, Limit).
|
||||||
|
|
||||||
|
select(Cont) ->
|
||||||
|
catch lets:select(Cont).
|
||||||
|
|
||||||
|
select_count(Tab, Spec) ->
|
||||||
|
catch lets:select_count(Tab, Spec).
|
||||||
|
|
||||||
|
select_delete(Tab, Spec) ->
|
||||||
|
catch lets:select_delete(Tab, Spec).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec) ->
|
||||||
|
catch lets:select_reverse(Tab, Spec).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec, Limit) ->
|
||||||
|
catch lets:select_reverse(Tab, Spec, Limit).
|
||||||
|
|
||||||
|
select_reverse(Cont) ->
|
||||||
|
catch lets:select_reverse(Cont).
|
||||||
|
|
||||||
tab2list(Tab) ->
|
tab2list(Tab) ->
|
||||||
catch lets:tab2list(Tab).
|
catch lets:tab2list(Tab).
|
||||||
|
|
|
@ -31,15 +31,37 @@
|
||||||
, new/3
|
, new/3
|
||||||
, destroy/3
|
, destroy/3
|
||||||
, repair/3
|
, repair/3
|
||||||
, insert/2
|
|
||||||
, insert_new/2
|
|
||||||
, delete/1
|
, delete/1
|
||||||
, delete/2
|
, delete/2
|
||||||
, delete_all_objects/1
|
, delete_all_objects/1
|
||||||
, lookup/2
|
|
||||||
, first/1
|
, first/1
|
||||||
, next/2
|
, foldl/3
|
||||||
|
, foldr/3
|
||||||
|
, info/1
|
||||||
, info/2
|
, info/2
|
||||||
|
, insert/2
|
||||||
|
, insert_new/2
|
||||||
|
, last/1
|
||||||
|
, lookup/2
|
||||||
|
, lookup_element/3
|
||||||
|
, match/2
|
||||||
|
, match/3
|
||||||
|
, match/1
|
||||||
|
, match_delete/2
|
||||||
|
, match_object/2
|
||||||
|
, match_object/3
|
||||||
|
, match_object/1
|
||||||
|
, member/2
|
||||||
|
, next/2
|
||||||
|
, prev/2
|
||||||
|
, select/2
|
||||||
|
, select/3
|
||||||
|
, select/1
|
||||||
|
, select_count/2
|
||||||
|
, select_delete/2
|
||||||
|
, select_reverse/2
|
||||||
|
, select_reverse/3
|
||||||
|
, select_reverse/1
|
||||||
, tab2list/1
|
, tab2list/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
@ -68,12 +90,6 @@ destroy(Tab, Name, Options) ->
|
||||||
repair(Tab, Name, Options) ->
|
repair(Tab, Name, Options) ->
|
||||||
qc_lets_raw:repair(Tab, Name, Options).
|
qc_lets_raw:repair(Tab, Name, Options).
|
||||||
|
|
||||||
insert(Tab, ObjOrObjs) ->
|
|
||||||
qc_lets_proxy:insert(Tab, ObjOrObjs).
|
|
||||||
|
|
||||||
insert_new(Tab, ObjOrObjs) ->
|
|
||||||
qc_lets_proxy:insert_new(Tab, ObjOrObjs).
|
|
||||||
|
|
||||||
delete(Tab) ->
|
delete(Tab) ->
|
||||||
qc_lets_proxy:delete(Tab).
|
qc_lets_proxy:delete(Tab).
|
||||||
|
|
||||||
|
@ -83,24 +99,96 @@ delete(Tab, Key) ->
|
||||||
delete_all_objects(Tab) ->
|
delete_all_objects(Tab) ->
|
||||||
qc_lets_proxy:delete_all_objects(Tab).
|
qc_lets_proxy:delete_all_objects(Tab).
|
||||||
|
|
||||||
|
first(Tab) ->
|
||||||
|
qc_lets_proxy:first(Tab).
|
||||||
|
|
||||||
|
foldl(Function, Acc0, Tab) ->
|
||||||
|
qc_lets_proxy:foldl(Function, Acc0, Tab).
|
||||||
|
|
||||||
|
foldr(Function, Acc0, Tab) ->
|
||||||
|
qc_lets_proxy:foldr(Function, Acc0, Tab).
|
||||||
|
|
||||||
|
info(Tab) ->
|
||||||
|
qc_lets_proxy:info(Tab).
|
||||||
|
|
||||||
|
info(Tab, Item) ->
|
||||||
|
qc_lets_proxy:info(Tab, Item).
|
||||||
|
|
||||||
|
insert(Tab, ObjOrObjs) ->
|
||||||
|
qc_lets_proxy:insert(Tab, ObjOrObjs).
|
||||||
|
|
||||||
|
insert_new(Tab, ObjOrObjs) ->
|
||||||
|
qc_lets_proxy:insert_new(Tab, ObjOrObjs).
|
||||||
|
|
||||||
|
last(Tab) ->
|
||||||
|
qc_lets_proxy:last(Tab).
|
||||||
|
|
||||||
lookup(Tab, Key) ->
|
lookup(Tab, Key) ->
|
||||||
qc_lets_proxy:lookup(Tab, Key).
|
qc_lets_proxy:lookup(Tab, Key).
|
||||||
|
|
||||||
first(Tab) ->
|
lookup_element(Tab, Key, Pos) ->
|
||||||
qc_lets_proxy:first(Tab).
|
qc_lets_proxy:lookup_element(Tab, Key, Pos).
|
||||||
|
|
||||||
|
match(Tab, Pattern) ->
|
||||||
|
qc_lets_proxy:match(Tab, Pattern).
|
||||||
|
|
||||||
|
match(Tab, Pattern, Limit) ->
|
||||||
|
qc_lets_proxy:match(Tab, Pattern, Limit).
|
||||||
|
|
||||||
|
match(Cont) ->
|
||||||
|
qc_lets_proxy:match(Cont).
|
||||||
|
|
||||||
|
match_delete(Tab, Pattern) ->
|
||||||
|
qc_lets_proxy:match_delete(Tab, Pattern).
|
||||||
|
|
||||||
|
match_object(Tab, Pattern) ->
|
||||||
|
qc_lets_proxy:match_object(Tab, Pattern).
|
||||||
|
|
||||||
|
match_object(Tab, Pattern, Limit) ->
|
||||||
|
qc_lets_proxy:match_object(Tab, Pattern, Limit).
|
||||||
|
|
||||||
|
match_object(Cont) ->
|
||||||
|
qc_lets_proxy:match_object(Cont).
|
||||||
|
|
||||||
|
member(Tab, Key) ->
|
||||||
|
qc_lets_proxy:member(Tab, Key).
|
||||||
|
|
||||||
next(Tab, Key) ->
|
next(Tab, Key) ->
|
||||||
qc_lets_proxy:next(Tab, Key).
|
qc_lets_proxy:next(Tab, Key).
|
||||||
|
|
||||||
info(Tab, Item) ->
|
prev(Tab, Key) ->
|
||||||
qc_lets_proxy:info(Tab, Item).
|
qc_lets_proxy:prev(Tab, Key).
|
||||||
|
|
||||||
|
select(Tab, Spec) ->
|
||||||
|
qc_lets_proxy:select(Tab, Spec).
|
||||||
|
|
||||||
|
select(Tab, Spec, Limit) ->
|
||||||
|
qc_lets_proxy:select(Tab, Spec, Limit).
|
||||||
|
|
||||||
|
select(Cont) ->
|
||||||
|
qc_lets_proxy:select(Cont).
|
||||||
|
|
||||||
|
select_count(Tab, Spec) ->
|
||||||
|
qc_lets_proxy:select_count(Tab, Spec).
|
||||||
|
|
||||||
|
select_delete(Tab, Spec) ->
|
||||||
|
qc_lets_proxy:select_delete(Tab, Spec).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec) ->
|
||||||
|
qc_lets_proxy:select_reverse(Tab, Spec).
|
||||||
|
|
||||||
|
select_reverse(Tab, Spec, Limit) ->
|
||||||
|
qc_lets_proxy:select_reverse(Tab, Spec, Limit).
|
||||||
|
|
||||||
|
select_reverse(Cont) ->
|
||||||
|
qc_lets_proxy:select_reverse(Cont).
|
||||||
|
|
||||||
tab2list(Tab) ->
|
tab2list(Tab) ->
|
||||||
qc_lets_proxy:tab2list(Tab).
|
qc_lets_proxy:tab2list(Tab).
|
||||||
|
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
||||||
stop_slave() ->
|
stop_slave() ->
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
, first/1, first/2
|
, first/1, first/2
|
||||||
, last/1, last/2
|
, last/1, last/2
|
||||||
, next/2, next/3
|
, next/2, next/3
|
||||||
|
, prev/2, prev/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
|
||||||
|
@ -291,6 +292,39 @@ next(Db, Key, Options) ->
|
||||||
free_ptr(LenPtr1)
|
free_ptr(LenPtr1)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
prev(Db, Key) ->
|
||||||
|
Options = leveldb:leveldb_readoptions_create(),
|
||||||
|
try
|
||||||
|
prev(Db, Key, Options)
|
||||||
|
after
|
||||||
|
leveldb:leveldb_readoptions_destroy(Options)
|
||||||
|
end.
|
||||||
|
|
||||||
|
prev(Db, Key, Options) ->
|
||||||
|
Iter = leveldb:leveldb_create_iterator(Db, Options),
|
||||||
|
LenPtr = lenptr(),
|
||||||
|
LenPtr1 = lenptr(),
|
||||||
|
try
|
||||||
|
leveldb:leveldb_iter_seek(Iter, binary_to_list(Key), byte_size(Key)),
|
||||||
|
case leveldb:leveldb_iter_valid(Iter) of
|
||||||
|
0 ->
|
||||||
|
last(Db, Options);
|
||||||
|
1 ->
|
||||||
|
leveldb:leveldb_iter_prev(Iter),
|
||||||
|
case leveldb:leveldb_iter_valid(Iter) of
|
||||||
|
0 ->
|
||||||
|
true;
|
||||||
|
1 ->
|
||||||
|
KeyPtr1 = leveldb:leveldb_iter_key(Iter, LenPtr1),
|
||||||
|
read_binary(KeyPtr1, LenPtr1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
after
|
||||||
|
leveldb:leveldb_iter_destroy(Iter),
|
||||||
|
free_ptr(LenPtr),
|
||||||
|
free_ptr(LenPtr1)
|
||||||
|
end.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal
|
%%% Internal
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
|
@ -28,10 +28,13 @@
|
||||||
-behaviour(qc_statem).
|
-behaviour(qc_statem).
|
||||||
-export([command_gen/2]).
|
-export([command_gen/2]).
|
||||||
-export([initial_state/0, state_is_sane/1, next_state/3, precondition/2, postcondition/3]).
|
-export([initial_state/0, state_is_sane/1, next_state/3, precondition/2, postcondition/3]).
|
||||||
-export([commands_setup/1, commands_teardown/1, commands_teardown/2]).
|
-export([setup/1, teardown/1, teardown/2, aggregate/1]).
|
||||||
|
|
||||||
%% @TODO remove at time of db, db_read, db_write options testing
|
%% @TODO remove at time of db, db_read, db_write options testing
|
||||||
|
%% DEBUG
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
|
%% Implementation
|
||||||
|
-export([match31/3, match_object31/3, select31/3, select_reverse31/3]).
|
||||||
|
|
||||||
%% @NOTE For boilerplate exports, see "qc_statem.hrl"
|
%% @NOTE For boilerplate exports, see "qc_statem.hrl"
|
||||||
-include_lib("qc/include/qc_statem.hrl").
|
-include_lib("qc/include/qc_statem.hrl").
|
||||||
|
@ -50,7 +53,13 @@
|
||||||
-define(FLOAT_KEYS, [float(Key) || Key <- ?INT_KEYS]).
|
-define(FLOAT_KEYS, [float(Key) || Key <- ?INT_KEYS]).
|
||||||
-define(BINARY_KEYS, [term_to_binary(Key) || Key <- ?INT_KEYS]).
|
-define(BINARY_KEYS, [term_to_binary(Key) || Key <- ?INT_KEYS]).
|
||||||
|
|
||||||
-record(obj, {key :: integer() | float() | binary(), val :: integer() | float() | binary()}).
|
-type key() :: integer() | float() | binary() | atom().
|
||||||
|
-type val() :: integer() | float() | binary() | atom().
|
||||||
|
|
||||||
|
-record(obj, {
|
||||||
|
key :: key(),
|
||||||
|
val :: val()
|
||||||
|
}).
|
||||||
|
|
||||||
-type obj() :: #obj{}.
|
-type obj() :: #obj{}.
|
||||||
-type ets_type() :: set | ordered_set. %% default is set
|
-type ets_type() :: set | ordered_set. %% default is set
|
||||||
|
@ -82,33 +91,53 @@ serial_command_gen(_Mod,#state{tab=undefined}=S) ->
|
||||||
%% @TODO ++ [{call,?IMPL,destroy,[undefined,?TAB,gen_options(destroy,S)]}]
|
%% @TODO ++ [{call,?IMPL,destroy,[undefined,?TAB,gen_options(destroy,S)]}]
|
||||||
%% @TODO ++ [{call,?IMPL,repair,[undefined,?TAB,gen_options(repair,S)]}]
|
%% @TODO ++ [{call,?IMPL,repair,[undefined,?TAB,gen_options(repair,S)]}]
|
||||||
);
|
);
|
||||||
serial_command_gen(_Mod,#state{tab=Tab, type=Type}=S) ->
|
serial_command_gen(_Mod,#state{tab=Tab, type=Type, impl=Impl}=S) ->
|
||||||
%% @TODO insert/3, insert_new/3, delete/3, delete_all_objs/2 write_gen_options
|
%% @TODO gen_db_write_options/2
|
||||||
%% @TODO lookup/3 read_gen_options
|
%% @TODO gen_db_read_options/2
|
||||||
|
%% @TODO info/1, info/2
|
||||||
oneof([{call,?IMPL,insert,[Tab,oneof([gen_obj(S),gen_objs(S)])]}]
|
oneof([{call,?IMPL,insert,[Tab,oneof([gen_obj(S),gen_objs(S)])]}]
|
||||||
++ [{call,?IMPL,insert_new,[Tab,oneof([gen_obj(S),gen_objs(S)])]} || Type =:= ets]
|
++ [{call,?IMPL,insert_new,[Tab,oneof([gen_obj(S),gen_objs(S)])]} || Impl =:= ets]
|
||||||
++ [{call,?IMPL,delete,[Tab]}]
|
++ [{call,?IMPL,delete,[Tab]}]
|
||||||
++ [{call,?IMPL,delete,[Tab,gen_key(S)]}]
|
++ [{call,?IMPL,delete,[Tab,gen_key(S)]}]
|
||||||
++ [{call,?IMPL,delete_all_objs,[Tab]} || Type =:= ets]
|
++ [{call,?IMPL,delete_all_objects,[Tab]} || Type =:= ets]
|
||||||
|
++ [{call,?IMPL,member,[Tab,gen_key(S)]}]
|
||||||
++ [{call,?IMPL,lookup,[Tab,gen_key(S)]}]
|
++ [{call,?IMPL,lookup,[Tab,gen_key(S)]}]
|
||||||
|
++ [{call,?IMPL,lookup_element,[Tab,gen_key(S),choose(1,record_info(size,obj))]}]
|
||||||
++ [{call,?IMPL,first,[Tab]}]
|
++ [{call,?IMPL,first,[Tab]}]
|
||||||
|
++ [{call,?IMPL,last,[Tab]}]
|
||||||
++ [{call,?IMPL,next,[Tab,gen_key(S)]}]
|
++ [{call,?IMPL,next,[Tab,gen_key(S)]}]
|
||||||
%% @TODO info
|
++ [{call,?IMPL,prev,[Tab,gen_key(S)]}]
|
||||||
|
++ [{call,?IMPL,foldl,[fun(X,Acc) -> [X|Acc] end, [], Tab]}]
|
||||||
|
++ [{call,?IMPL,foldr,[fun(X,Acc) -> [X|Acc] end, [], Tab]}]
|
||||||
++ [{call,?IMPL,tab2list,[Tab]}]
|
++ [{call,?IMPL,tab2list,[Tab]}]
|
||||||
|
++ [{call,?IMPL,match,[Tab, gen_pattern(S)]}]
|
||||||
|
%%++ [{call,?MODULE,match31,[Tab, gen_pattern(S), gen_pos_integer()]}]
|
||||||
|
++ [{call,?IMPL,match_delete,[Tab, gen_pattern(S)]}]
|
||||||
|
++ [{call,?IMPL,match_object,[Tab, gen_pattern(S)]}]
|
||||||
|
%%++ [{call,?MODULE,match_object31,[Tab, gen_pattern(S), gen_pos_integer()]}]
|
||||||
|
++ [{call,?IMPL,select,[Tab, gen_spec(S)]}]
|
||||||
|
++ [{call,?MODULE,select31,[Tab, gen_spec(S), gen_pos_integer()]}]
|
||||||
|
++ [{call,?IMPL,select_count,[Tab, gen_spec_true(S)]}]
|
||||||
|
++ [{call,?IMPL,select_delete,[Tab, gen_spec_true(S)]}]
|
||||||
|
++ [{call,?IMPL,select_reverse,[Tab, gen_spec(S)]}]
|
||||||
|
%%++ [{call,?MODULE,select_reverse31,[Tab, gen_spec(S), gen_pos_integer()]}]
|
||||||
).
|
).
|
||||||
|
|
||||||
parallel_command_gen(_Mod,#state{tab=undefined, type=undefined, impl=undefined}=S) ->
|
parallel_command_gen(_Mod,#state{tab=undefined, type=undefined, impl=undefined}=S) ->
|
||||||
{call,?IMPL,new,[?TAB,gen_options(new,S)]};
|
{call,?IMPL,new,[?TAB,gen_options(new,S)]};
|
||||||
parallel_command_gen(_Mod,#state{tab=Tab, type=Type}=S) ->
|
parallel_command_gen(_Mod,#state{tab=Tab, type=Type}=S) ->
|
||||||
%% @TODO insert/3, insert_new/3, delete_all_objs/2 write_gen_options
|
%% @TODO gen_db_write_options/2
|
||||||
%% @TODO lookup/3 read_gen_options
|
%% @TODO gen_db_read_options/2
|
||||||
oneof([{call,?IMPL,insert,[Tab,oneof([gen_obj(S),gen_objs(S)])]}]
|
oneof([{call,?IMPL,insert,[Tab,oneof([gen_obj(S),gen_objs(S)])]}]
|
||||||
++ [{call,?IMPL,insert_new,[Tab,oneof([gen_obj(S),gen_objs(S)])]} || Type =:= ets]
|
++ [{call,?IMPL,insert_new,[Tab,oneof([gen_obj(S),gen_objs(S)])]} || Type =:= ets]
|
||||||
++ [{call,?IMPL,delete,[Tab,gen_key(S)]}]
|
++ [{call,?IMPL,delete,[Tab,gen_key(S)]}]
|
||||||
++ [{call,?IMPL,delete_all_objs,[Tab]} || Type =:= ets]
|
++ [{call,?IMPL,member,[Tab,gen_key(S)]}]
|
||||||
++ [{call,?IMPL,lookup,[Tab,gen_key(S)]}]
|
++ [{call,?IMPL,lookup,[Tab,gen_key(S)]}]
|
||||||
|
++ [{call,?IMPL,lookup_element,[Tab,gen_key(S),choose(1,record_info(size,obj))]}]
|
||||||
++ [{call,?IMPL,first,[Tab]}]
|
++ [{call,?IMPL,first,[Tab]}]
|
||||||
|
++ [{call,?IMPL,last,[Tab]}]
|
||||||
++ [{call,?IMPL,next,[Tab,gen_key(S)]}]
|
++ [{call,?IMPL,next,[Tab,gen_key(S)]}]
|
||||||
|
++ [{call,?IMPL,prev,[Tab,gen_key(S)]}]
|
||||||
).
|
).
|
||||||
|
|
||||||
-spec initial_state() -> #state{}.
|
-spec initial_state() -> #state{}.
|
||||||
|
@ -175,30 +204,41 @@ next_state(#state{exists=Exists}=S, _V, {call,_,delete,[_Tab]}) ->
|
||||||
S#state{tab=undefined, exists=Exists};
|
S#state{tab=undefined, exists=Exists};
|
||||||
next_state(S, _V, {call,_,delete,[_Tab,Key]}) ->
|
next_state(S, _V, {call,_,delete,[_Tab,Key]}) ->
|
||||||
S#state{objs=keydelete(Key, S)};
|
S#state{objs=keydelete(Key, S)};
|
||||||
next_state(#state{impl=ets}=S, _V, {call,_,delete_all_objs,[_Tab]}) ->
|
next_state(#state{impl=ets}=S, _V, {call,_,delete_all_objects,[_Tab]}) ->
|
||||||
S#state{objs=[]};
|
S#state{objs=[]};
|
||||||
next_state(S, _V, {call,_,delete_all_objs,[_Tab]}) ->
|
next_state(S, _V, {call,_,delete_all_objects,[_Tab]}) ->
|
||||||
S;
|
S;
|
||||||
|
next_state(S, _V, {call,_,match_delete,[_Tab,Pattern]}) ->
|
||||||
|
match_delete(S, Pattern);
|
||||||
|
next_state(S, _V, {call,_,select_delete,[_Tab,Spec]}) ->
|
||||||
|
select_delete(S, Spec);
|
||||||
next_state(S, _V, {call,_,_,_}) ->
|
next_state(S, _V, {call,_,_,_}) ->
|
||||||
S.
|
S.
|
||||||
|
|
||||||
-spec precondition(#state{}, tuple()) -> boolean().
|
-spec precondition(#state{}, tuple()) -> boolean().
|
||||||
precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,new,[?TAB,Options]}) ->
|
precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,new,[?TAB,Options]}) ->
|
||||||
|
Drv = proplists:get_bool(drv, Options),
|
||||||
|
Nif = proplists:get_bool(nif, Options),
|
||||||
|
if Drv orelse Nif ->
|
||||||
L = proplists:get_value(db, Options, []),
|
L = proplists:get_value(db, Options, []),
|
||||||
proplists:get_bool(create_if_missing, L) andalso proplists:get_bool(error_if_exists, L);
|
proplists:get_bool(create_if_missing, L) andalso proplists:get_bool(error_if_exists, L);
|
||||||
precondition(#state{tab=Tab}, {call,_,new,[?TAB,_Options]}) ->
|
true ->
|
||||||
Tab =:= undefined;
|
true
|
||||||
precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,new,[_Tab,?TAB,Options]}) ->
|
end;
|
||||||
L = proplists:get_value(db, Options, []),
|
precondition(#state{tab=_Tab}, {call,_,new,[?TAB,_Options]}) ->
|
||||||
proplists:get_bool(create_if_missing, L) andalso proplists:get_bool(error_if_exists, L);
|
false;
|
||||||
precondition(#state{tab=Tab}, {call,_,new,[_Tab,?TAB,_Options]}) ->
|
|
||||||
Tab =:= undefined;
|
|
||||||
precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,new,[_Tab,?TAB,_Options]}) ->
|
precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,new,[_Tab,?TAB,_Options]}) ->
|
||||||
false;
|
false;
|
||||||
|
precondition(#state{tab=Tab}, {call,_,new,[_Tab,?TAB,_Options]}) ->
|
||||||
|
Tab =:= undefined;
|
||||||
precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,destroy,[_Tab,?TAB,_Options]}) ->
|
precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,destroy,[_Tab,?TAB,_Options]}) ->
|
||||||
false;
|
false;
|
||||||
|
precondition(#state{tab=Tab}, {call,_,destroy,[_Tab,?TAB,_Options]}) ->
|
||||||
|
Tab =:= undefined;
|
||||||
precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,repair,[_Tab,?TAB,_Options]}) ->
|
precondition(#state{tab=undefined, type=undefined, impl=undefined}, {call,_,repair,[_Tab,?TAB,_Options]}) ->
|
||||||
false;
|
false;
|
||||||
|
precondition(#state{tab=Tab}, {call,_,repair,[_Tab,?TAB,_Options]}) ->
|
||||||
|
Tab =:= undefined;
|
||||||
precondition(_S, {call,_,_,_}) ->
|
precondition(_S, {call,_,_,_}) ->
|
||||||
true.
|
true.
|
||||||
|
|
||||||
|
@ -230,12 +270,18 @@ postcondition(_S, {call,_,delete,[_Tab]}, Res) ->
|
||||||
Res =:= true;
|
Res =:= true;
|
||||||
postcondition(_S, {call,_,delete,[_Tab,_Key]}, Res) ->
|
postcondition(_S, {call,_,delete,[_Tab,_Key]}, Res) ->
|
||||||
Res =:= true;
|
Res =:= true;
|
||||||
postcondition(#state{impl=ets}=_S, {call,_,delete_all_objs,[_Tab]}, Res) ->
|
postcondition(#state{impl=ets}=_S, {call,_,delete_all_objects,[_Tab]}, Res) ->
|
||||||
Res =:= true;
|
Res =:= true;
|
||||||
postcondition(_S, {call,_,delete_all_objs,[_Tab]}, {'EXIT',{badarg,_}}) ->
|
postcondition(_S, {call,_,delete_all_objects,[_Tab]}, {'EXIT',{badarg,_}}) ->
|
||||||
true;
|
true;
|
||||||
|
postcondition(S, {call,_,member,[_Tab,Key]}, Res) ->
|
||||||
|
Res =:= keymember(Key, S);
|
||||||
postcondition(S, {call,_,lookup,[_Tab,Key]}, Res) ->
|
postcondition(S, {call,_,lookup,[_Tab,Key]}, Res) ->
|
||||||
Res =:= keyfind(Key, S);
|
Res =:= keyfind(Key, S);
|
||||||
|
postcondition(S, {call,_,lookup_element,[_Tab,Key,_Pos]}, {'EXIT',{badarg,_}}) ->
|
||||||
|
not keymember(Key, S);
|
||||||
|
postcondition(S, {call,_,lookup_element,[_Tab,Key,Pos]}, Res) ->
|
||||||
|
[Res] =:= keyposfind(Key, Pos, S);
|
||||||
postcondition(#state{objs=[]}, {call,_,first,[_Tab]}, Res) ->
|
postcondition(#state{objs=[]}, {call,_,first,[_Tab]}, Res) ->
|
||||||
Res =:= '$end_of_table';
|
Res =:= '$end_of_table';
|
||||||
postcondition(#state{type=set}=S, {call,_,first,[_Tab]}, Res) ->
|
postcondition(#state{type=set}=S, {call,_,first,[_Tab]}, Res) ->
|
||||||
|
@ -243,6 +289,13 @@ postcondition(#state{type=set}=S, {call,_,first,[_Tab]}, Res) ->
|
||||||
postcondition(#state{type=ordered_set}=S, {call,_,first,[_Tab]}, Res) ->
|
postcondition(#state{type=ordered_set}=S, {call,_,first,[_Tab]}, Res) ->
|
||||||
#obj{key=K} = hd(sort(S)),
|
#obj{key=K} = hd(sort(S)),
|
||||||
Res =:= K;
|
Res =:= K;
|
||||||
|
postcondition(#state{objs=[]}, {call,_,last,[_Tab]}, Res) ->
|
||||||
|
Res =:= '$end_of_table';
|
||||||
|
postcondition(#state{type=set}=S, {call,_,last,[_Tab]}, Res) ->
|
||||||
|
keymember(Res, S);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,last,[_Tab]}, Res) ->
|
||||||
|
#obj{key=K} = hd(rsort(S)),
|
||||||
|
Res =:= K;
|
||||||
postcondition(#state{impl=ets, type=set}=S, {call,_,next,[_Tab, Key]}, {'EXIT',{badarg,_}}) ->
|
postcondition(#state{impl=ets, type=set}=S, {call,_,next,[_Tab, Key]}, {'EXIT',{badarg,_}}) ->
|
||||||
not keymember(Key, S);
|
not keymember(Key, S);
|
||||||
postcondition(#state{impl=ets, type=set}=S, {call,_,next,[_Tab, Key]}, '$end_of_table') ->
|
postcondition(#state{impl=ets, type=set}=S, {call,_,next,[_Tab, Key]}, '$end_of_table') ->
|
||||||
|
@ -253,8 +306,6 @@ postcondition(#state{type=set}, {call,_,next,[_Tab, _Key]}, '$end_of_table') ->
|
||||||
true;
|
true;
|
||||||
postcondition(#state{type=set}=S, {call,_,next,[_Tab, _Key]}, Res) ->
|
postcondition(#state{type=set}=S, {call,_,next,[_Tab, _Key]}, Res) ->
|
||||||
keymember(Res, S);
|
keymember(Res, S);
|
||||||
postcondition(#state{type=ordered_set, objs=[]}, {call,_,next,[_Tab, _Key]}, Res) ->
|
|
||||||
Res =:= '$end_of_table';
|
|
||||||
postcondition(#state{type=ordered_set}=S, {call,_,next,[_Tab, Key]}, Res) ->
|
postcondition(#state{type=ordered_set}=S, {call,_,next,[_Tab, Key]}, Res) ->
|
||||||
case lists:dropwhile(fun(#obj{key=X}) -> lteq(X, Key, S) end, sort(S)) of
|
case lists:dropwhile(fun(#obj{key=X}) -> lteq(X, Key, S) end, sort(S)) of
|
||||||
[] ->
|
[] ->
|
||||||
|
@ -262,30 +313,103 @@ postcondition(#state{type=ordered_set}=S, {call,_,next,[_Tab, Key]}, Res) ->
|
||||||
[#obj{key=K}|_] ->
|
[#obj{key=K}|_] ->
|
||||||
Res =:= K
|
Res =:= K
|
||||||
end;
|
end;
|
||||||
|
postcondition(#state{impl=ets, type=set}=S, {call,_,prev,[_Tab, Key]}, {'EXIT',{badarg,_}}) ->
|
||||||
|
not keymember(Key, S);
|
||||||
|
postcondition(#state{impl=ets, type=set}=S, {call,_,prev,[_Tab, Key]}, '$end_of_table') ->
|
||||||
|
keymember(Key, S);
|
||||||
|
postcondition(#state{impl=ets, type=set}=S, {call,_,prev,[_Tab, Key]}, Res) ->
|
||||||
|
keymember(Key, S) andalso keymember(Res, S);
|
||||||
|
postcondition(#state{type=set}, {call,_,prev,[_Tab, _Key]}, '$end_of_table') ->
|
||||||
|
true;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,prev,[_Tab, _Key]}, Res) ->
|
||||||
|
keymember(Res, S);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,prev,[_Tab, Key]}, Res) ->
|
||||||
|
case lists:dropwhile(fun(#obj{key=X}) -> gteq(X, Key, S) end, rsort(S)) of
|
||||||
|
[] ->
|
||||||
|
Res =:= '$end_of_table';
|
||||||
|
[#obj{key=K}|_] ->
|
||||||
|
Res =:= K
|
||||||
|
end;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,foldl,[_Function,_Acc0,_Tab]}, Res) ->
|
||||||
|
[] =:= (S#state.objs -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,foldl,[_Function,_Acc0,_Tab]}, Res) ->
|
||||||
|
rsort(S) =:= Res;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,foldr,[_Function,_Acc0,_Tab]}, Res) ->
|
||||||
|
[] =:= (S#state.objs -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,foldr,[_Function,_Acc0,_Tab]}, Res) ->
|
||||||
|
sort(S) =:= Res;
|
||||||
postcondition(#state{type=set}=S, {call,_,tab2list,[_Tab]}, Res) ->
|
postcondition(#state{type=set}=S, {call,_,tab2list,[_Tab]}, Res) ->
|
||||||
[] =:= (S#state.objs -- Res);
|
[] =:= (S#state.objs -- Res);
|
||||||
postcondition(#state{type=ordered_set}=S, {call,_,tab2list,[_Tab]}, Res) ->
|
postcondition(#state{type=ordered_set}=S, {call,_,tab2list,[_Tab]}, Res) ->
|
||||||
sort(S) =:= Res;
|
sort(S) =:= Res;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,match,[_Tab,Pattern]}, Res) ->
|
||||||
|
[] =:= (match(S, Pattern) -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,match,[_Tab,Pattern]}, Res) ->
|
||||||
|
match(S, Pattern) =:= Res;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,match31,[_Tab,Pattern,_Limit]}, Res) ->
|
||||||
|
[] =:= (match(S, Pattern) -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,match31,[_Tab,Pattern,_Limit]}, Res) ->
|
||||||
|
match(S, Pattern) =:= Res;
|
||||||
|
postcondition(_S, {call,_,match_delete,[_Tab,_Pattern]}, Res) ->
|
||||||
|
Res;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,match_object,[_Tab,Pattern]}, Res) ->
|
||||||
|
[] =:= (match_object(S, Pattern) -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,match_object,[_Tab,Pattern]}, Res) ->
|
||||||
|
match_object(S, Pattern) =:= Res;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,match_object31,[_Tab,Pattern,_Limit]}, Res) ->
|
||||||
|
[] =:= (match_object(S, Pattern) -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,match_object31,[_Tab,Pattern,_Limit]}, Res) ->
|
||||||
|
match_object(S, Pattern) =:= Res;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,select,[_Tab,Spec]}, Res) ->
|
||||||
|
[] =:= (select(S, Spec) -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,select,[_Tab,Spec]}, Res) ->
|
||||||
|
select(S, Spec) =:= Res;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,select31,[_Tab,Spec,_Limit]}, Res) ->
|
||||||
|
[] =:= (select(S, Spec) -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,select31,[_Tab,Spec,_Limit]}, Res) ->
|
||||||
|
select(S, Spec) =:= Res;
|
||||||
|
postcondition(S, {call,_,select_count,[_Tab,Spec]}, Res) ->
|
||||||
|
select_count(S, Spec) =:= Res;
|
||||||
|
postcondition(S, {call,_,select_delete,[_Tab,Spec]}, Res) ->
|
||||||
|
select_count(S, Spec) =:= Res;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,select_reverse,[_Tab,Spec]}, Res) ->
|
||||||
|
[] =:= (select_reverse(S, Spec) -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,select_reverse,[_Tab,Spec]}, Res) ->
|
||||||
|
select_reverse(S, Spec) =:= Res;
|
||||||
|
postcondition(#state{type=set}=S, {call,_,select_reverse31,[_Tab,Spec,_Limit]}, Res) ->
|
||||||
|
[] =:= (select_reverse(S, Spec) -- Res);
|
||||||
|
postcondition(#state{type=ordered_set}=S, {call,_,select_reverse31,[_Tab,Spec,_Limit]}, Res) ->
|
||||||
|
select_reverse(S, Spec) =:= Res;
|
||||||
postcondition(_S, {call,_,_,_}, _Res) ->
|
postcondition(_S, {call,_,_,_}, _Res) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
-spec commands_setup(boolean()) -> {ok, term()}.
|
-spec setup(boolean()) -> {ok, term()}.
|
||||||
commands_setup(_Hard) ->
|
setup(_Hard) ->
|
||||||
?IMPL:teardown(?TAB),
|
?IMPL:teardown(?TAB),
|
||||||
{ok, unused}.
|
{ok, unused}.
|
||||||
|
|
||||||
-spec commands_teardown(term()) -> ok.
|
-spec teardown(term()) -> ok.
|
||||||
commands_teardown(unused) ->
|
teardown(unused) ->
|
||||||
?IMPL:teardown(?TAB),
|
?IMPL:teardown(?TAB),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec commands_teardown(term(), #state{}) -> ok.
|
-spec teardown(term(), #state{}) -> ok.
|
||||||
commands_teardown(Ref, _State) ->
|
teardown(Ref, _State) ->
|
||||||
commands_teardown(Ref).
|
teardown(Ref).
|
||||||
|
|
||||||
|
-spec aggregate([{integer(), term(), term(), #state{}}])
|
||||||
|
-> [{atom(), atom(), integer() | term()}].
|
||||||
|
aggregate(L) ->
|
||||||
|
[ {Cmd,filter_reply(Reply)} || {_N,{set,_,{call,_,Cmd,_}},Reply,_State} <- L ].
|
||||||
|
|
||||||
|
filter_reply({'EXIT',{Err,_}}) ->
|
||||||
|
{error,Err};
|
||||||
|
filter_reply(_) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Internal
|
%%% Internal - Generators
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
gen_options(Op,#state{tab=undefined, type=undefined, impl=undefined}=S) ->
|
gen_options(Op,#state{tab=undefined, type=undefined, impl=undefined}=S) ->
|
||||||
|
@ -335,7 +459,7 @@ gen_boolean() ->
|
||||||
oneof([true, false]).
|
oneof([true, false]).
|
||||||
|
|
||||||
gen_pos_integer() ->
|
gen_pos_integer() ->
|
||||||
nat().
|
?LET(N, nat(), N+1).
|
||||||
|
|
||||||
gen_ets_type() ->
|
gen_ets_type() ->
|
||||||
noshrink(oneof([set, ordered_set])).
|
noshrink(oneof([set, ordered_set])).
|
||||||
|
@ -379,6 +503,23 @@ gen_obj(#state{objs=Objs}) ->
|
||||||
gen_objs(S) ->
|
gen_objs(S) ->
|
||||||
frequency([{9, non_empty(list(gen_obj(S)))}, {1, list(gen_obj(S))}]).
|
frequency([{9, non_empty(list(gen_obj(S)))}, {1, list(gen_obj(S))}]).
|
||||||
|
|
||||||
|
gen_pattern(S) ->
|
||||||
|
oneof([{'$1', '$2', '$3'}
|
||||||
|
, #obj{key=oneof(['_',gen_key(S)]), val='$1'}
|
||||||
|
, #obj{key='$1', val=oneof(['_',gen_val()])}
|
||||||
|
]).
|
||||||
|
|
||||||
|
gen_spec(S) ->
|
||||||
|
[{gen_pattern(S), [], [oneof(['$$', '$_'])]}].
|
||||||
|
|
||||||
|
gen_spec_true(S) ->
|
||||||
|
[{gen_pattern(S), [], [true]}].
|
||||||
|
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% Internal - Model
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
insert_objs(S, []) ->
|
insert_objs(S, []) ->
|
||||||
S;
|
S;
|
||||||
insert_objs(S, [#obj{key=K}=Obj|T]) ->
|
insert_objs(S, [#obj{key=K}=Obj|T]) ->
|
||||||
|
@ -426,11 +567,142 @@ keydelete(X, L, S) ->
|
||||||
keyreplace(X, Y, #state{objs=L}=S) ->
|
keyreplace(X, Y, #state{objs=L}=S) ->
|
||||||
lists:map(fun(Z=#obj{key=K}) -> case eq(X, K, S) of true -> Y; false -> Z end end, L).
|
lists:map(fun(Z=#obj{key=K}) -> case eq(X, K, S) of true -> Y; false -> Z end end, L).
|
||||||
|
|
||||||
|
keymember(X, S) ->
|
||||||
|
[] =/= keyfind(X, S).
|
||||||
|
|
||||||
keyfind(X, #state{objs=L}=S) ->
|
keyfind(X, #state{objs=L}=S) ->
|
||||||
lists:filter(fun(#obj{key=K}) -> eq(X, K, S) end, L).
|
lists:filter(fun(#obj{key=K}) -> eq(X, K, S) end, L).
|
||||||
|
|
||||||
keymember(X, S) ->
|
keyposfind(X, Pos, S) ->
|
||||||
[] =/= keyfind(X, S).
|
[ element(Pos, Obj) || Obj <- keyfind(X, S) ].
|
||||||
|
|
||||||
|
match(S, Pattern) ->
|
||||||
|
select(S, [{Pattern, [], ['$$']}]).
|
||||||
|
|
||||||
|
match(S, Pattern, Limit) ->
|
||||||
|
select(S, [{Pattern, [], ['$$']}], Limit).
|
||||||
|
|
||||||
|
match_cont(S, Pattern, Limit, StartKey) ->
|
||||||
|
ContObjs = lists:dropwhile(fun(#obj{key=X}) -> lteq(X, StartKey, S) end, sort(S)),
|
||||||
|
match(S#state{objs=ContObjs}, Pattern, Limit).
|
||||||
|
|
||||||
|
match_delete(#state{objs=L}=S, Pattern) ->
|
||||||
|
S#state{objs=L -- match_object(S, Pattern)}.
|
||||||
|
|
||||||
|
match_object(S, Pattern) ->
|
||||||
|
match_object(S, Pattern, undefined).
|
||||||
|
|
||||||
|
match_object(S, Pattern, Limit) ->
|
||||||
|
select(S, [{Pattern, [], ['$_']}], Limit).
|
||||||
|
|
||||||
|
match_object_cont(S, Pattern, Limit, StartKey) ->
|
||||||
|
ContObjs = lists:dropwhile(fun(#obj{key=X}) -> lteq(X, StartKey, S) end, sort(S)),
|
||||||
|
match_object(S#state{objs=ContObjs}, Pattern, Limit).
|
||||||
|
|
||||||
|
match_object_reverse(S, Pattern) ->
|
||||||
|
match_object_reverse(S, Pattern, undefined).
|
||||||
|
|
||||||
|
match_object_reverse(S, Pattern, Limit) ->
|
||||||
|
select_reverse(S, [{Pattern, [], ['$_']}], Limit).
|
||||||
|
|
||||||
|
match_object_reverse_cont(S, Pattern, Limit, StartKey) ->
|
||||||
|
ContObjs = lists:dropwhile(fun(#obj{key=X}) -> gteq(X, StartKey, S) end, rsort(S)),
|
||||||
|
match_object_reverse(S#state{objs=ContObjs}, Pattern, Limit).
|
||||||
|
|
||||||
|
select(S, Spec) ->
|
||||||
|
select(S, Spec, undefined).
|
||||||
|
|
||||||
|
select(S, Spec, Limit) ->
|
||||||
|
case select1(S, sort(S), Spec) of
|
||||||
|
[] ->
|
||||||
|
[];
|
||||||
|
Match when Limit =:= undefined ->
|
||||||
|
Match;
|
||||||
|
Match ->
|
||||||
|
lists:sublist(Match, Limit)
|
||||||
|
end.
|
||||||
|
|
||||||
|
select_cont(S, Spec, Limit, StartKey) ->
|
||||||
|
ContObjs = lists:dropwhile(fun(#obj{key=X}) -> lteq(X, StartKey, S) end, sort(S)),
|
||||||
|
select(S#state{objs=ContObjs}, Spec, Limit).
|
||||||
|
|
||||||
|
select_count(S, Spec) ->
|
||||||
|
select1(S, sort(S), Spec).
|
||||||
|
|
||||||
|
select_delete(#state{objs=L}=S, [{Pattern, [], [true]}]) ->
|
||||||
|
S#state{objs=L -- match_object(S, Pattern)}.
|
||||||
|
|
||||||
|
select_reverse(S, Spec) ->
|
||||||
|
select_reverse(S, Spec, undefined).
|
||||||
|
|
||||||
|
select_reverse(S, Spec, Limit) ->
|
||||||
|
case select1(S, rsort(S), Spec) of
|
||||||
|
[] ->
|
||||||
|
[];
|
||||||
|
Match when Limit =:= undefined ->
|
||||||
|
Match;
|
||||||
|
Match ->
|
||||||
|
lists:sublist(Match, Limit)
|
||||||
|
end.
|
||||||
|
|
||||||
|
select_reverse_cont(S, Spec, Limit, StartKey) ->
|
||||||
|
ContObjs = lists:dropwhile(fun(#obj{key=X}) -> gteq(X, StartKey, S) end, rsort(S)),
|
||||||
|
select_reverse(S#state{objs=ContObjs}, Spec, Limit).
|
||||||
|
|
||||||
|
select1(#state{impl=ets}=S, L, Spec) ->
|
||||||
|
select2(S#state{type=set}, L, Spec);
|
||||||
|
select1(S, L, Spec) ->
|
||||||
|
select2(S, L, Spec).
|
||||||
|
|
||||||
|
%% simple and limited select implementation
|
||||||
|
select2(S, L, [{Pattern, [], [Result]}]) ->
|
||||||
|
case Pattern of
|
||||||
|
{'$1', '$2', '$3'} ->
|
||||||
|
case Result of
|
||||||
|
'$$' ->
|
||||||
|
[ [obj, X, Y] || #obj{key=X,val=Y} <- L ];
|
||||||
|
'$_' ->
|
||||||
|
[ X || X <- L ];
|
||||||
|
true ->
|
||||||
|
length(L)
|
||||||
|
end;
|
||||||
|
#obj{key='_', val='$1'} ->
|
||||||
|
case Result of
|
||||||
|
'$$' ->
|
||||||
|
[ [X] || #obj{val=X} <- L ];
|
||||||
|
'$_' ->
|
||||||
|
[ X || X <- L ];
|
||||||
|
true ->
|
||||||
|
length(L)
|
||||||
|
end;
|
||||||
|
#obj{key='$1', val='_'} ->
|
||||||
|
case Result of
|
||||||
|
'$$' ->
|
||||||
|
[ [X] || #obj{key=X} <- L ];
|
||||||
|
'$_' ->
|
||||||
|
[ X || X <- L ];
|
||||||
|
true ->
|
||||||
|
length(L)
|
||||||
|
end;
|
||||||
|
#obj{key=P, val='$1'} ->
|
||||||
|
case Result of
|
||||||
|
'$$' ->
|
||||||
|
[ [X] || #obj{key=Y, val=X} <- L, eq(Y, P, S) ];
|
||||||
|
'$_' ->
|
||||||
|
[ X || #obj{key=Y}=X <- L, eq(Y, P, S) ];
|
||||||
|
true ->
|
||||||
|
length([ X || #obj{key=Y}=X <- L, eq(Y, P, S) ])
|
||||||
|
end;
|
||||||
|
#obj{key='$1', val=P} ->
|
||||||
|
case Result of
|
||||||
|
'$$' ->
|
||||||
|
[ [X] || #obj{key=X, val=Y} <- L, eq(Y, P, S) ];
|
||||||
|
'$_' ->
|
||||||
|
[ X || #obj{val=Y}=X <- L, eq(Y, P, S) ];
|
||||||
|
true ->
|
||||||
|
length([ X || #obj{val=Y}=X <- L, eq(Y, P, S) ])
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
eq(X, Y, #state{type=set, impl=ets}) ->
|
eq(X, Y, #state{type=set, impl=ets}) ->
|
||||||
X =:= Y;
|
X =:= Y;
|
||||||
|
@ -457,9 +729,69 @@ lteq(X, Y, #state{type=set}) ->
|
||||||
lteq(X, Y, #state{type=ordered_set}) ->
|
lteq(X, Y, #state{type=ordered_set}) ->
|
||||||
sext:encode(X) =< sext:encode(Y).
|
sext:encode(X) =< sext:encode(Y).
|
||||||
|
|
||||||
|
gteq(X, Y, #state{impl=ets}) ->
|
||||||
|
X >= Y;
|
||||||
|
gteq(X, Y, #state{type=set}) ->
|
||||||
|
term_to_binary(X) >= term_to_binary(Y);
|
||||||
|
gteq(X, Y, #state{type=ordered_set}) ->
|
||||||
|
sext:encode(X) >= sext:encode(Y).
|
||||||
|
|
||||||
sort(#state{impl=ets, objs=L}) ->
|
sort(#state{impl=ets, objs=L}) ->
|
||||||
lists:sort(L);
|
lists:sort(L);
|
||||||
sort(#state{objs=L}) ->
|
sort(#state{objs=L}) ->
|
||||||
[ sext:decode(X) || X <- lists:sort([ sext:encode(Y) || Y <- L ]) ].
|
[ sext:decode(X) || X <- lists:sort([ sext:encode(Y) || Y <- L ]) ].
|
||||||
|
|
||||||
|
rsort(S) ->
|
||||||
|
lists:reverse(sort(S)).
|
||||||
|
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% Internal - Implementation
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% @NOTE simplify test model by combining match/3 and match/1 into
|
||||||
|
%% single match31/3 command
|
||||||
|
|
||||||
|
match31(Tab, Pattern, Limit) ->
|
||||||
|
catch match31(?IMPL:match(Tab, Pattern, Limit), Pattern, Limit, []).
|
||||||
|
|
||||||
|
match31('$end_of_table', _Pattern, _Limit, Acc) ->
|
||||||
|
Acc;
|
||||||
|
match31({Match, Cont}, Pattern, Limit, Acc) when length(Match) =< Limit ->
|
||||||
|
match31(?IMPL:match(Cont), Pattern, Limit, Acc ++ Match).
|
||||||
|
|
||||||
|
%% @NOTE simplify test model by combining match_object/3 and
|
||||||
|
%% match_object/1 into single match_object31/3 command
|
||||||
|
|
||||||
|
match_object31(Tab, Pattern, Limit) ->
|
||||||
|
catch match_object31(?IMPL:match_object(Tab, Pattern, Limit), Pattern, Limit, []).
|
||||||
|
|
||||||
|
match_object31('$end_of_table', _Pattern, _Limit, Acc) ->
|
||||||
|
Acc;
|
||||||
|
match_object31({Match, Cont}, Pattern, Limit, Acc) when length(Match) =< Limit ->
|
||||||
|
match_object31(?IMPL:match_object(Cont), Pattern, Limit, Acc ++ Match).
|
||||||
|
|
||||||
|
%% @NOTE simplify test model by combining select/3 and select/1 into
|
||||||
|
%% single select31/3 command
|
||||||
|
|
||||||
|
select31(Tab, Spec, Limit) ->
|
||||||
|
catch select31(?IMPL:select(Tab, Spec, Limit), Spec, Limit, []).
|
||||||
|
|
||||||
|
select31('$end_of_table', _Spec, _Limit, Acc) ->
|
||||||
|
Acc;
|
||||||
|
select31({Match, Cont}, Spec, Limit, Acc) when length(Match) =< Limit ->
|
||||||
|
select31(?IMPL:select(Cont), Spec, Limit, Acc ++ Match).
|
||||||
|
|
||||||
|
%% @NOTE simplify test model by combining select_reverse/3 and
|
||||||
|
%% select_reverse/1 into single select_reverse31/3 command
|
||||||
|
|
||||||
|
select_reverse31(Tab, Spec, Limit) ->
|
||||||
|
catch select_reverse31(?IMPL:select_reverse(Tab, Spec, Limit), Spec, Limit, []).
|
||||||
|
|
||||||
|
select_reverse31('$end_of_table', _Spec, _Limit, Acc) ->
|
||||||
|
Acc;
|
||||||
|
select_reverse31({Match, Cont}, Spec, Limit, Acc) when length(Match) =< Limit ->
|
||||||
|
select_reverse31(?IMPL:select_reverse(Cont), Spec, Limit, Acc ++ Match).
|
||||||
|
|
||||||
|
|
||||||
-endif. %% -ifdef(QC).
|
-endif. %% -ifdef(QC).
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
-behaviour(qc_statem).
|
-behaviour(qc_statem).
|
||||||
-export([command_gen/2]).
|
-export([command_gen/2]).
|
||||||
-export([initial_state/0, state_is_sane/1, next_state/3, precondition/2, postcondition/3]).
|
-export([initial_state/0, state_is_sane/1, next_state/3, precondition/2, postcondition/3]).
|
||||||
-export([commands_setup/1, commands_teardown/1, commands_teardown/2]).
|
-export([setup/1, teardown/1, teardown/2, aggregate/1]).
|
||||||
|
|
||||||
%% @NOTE For boilerplate exports, see "qc_statem.hrl"
|
%% @NOTE For boilerplate exports, see "qc_statem.hrl"
|
||||||
-include_lib("eqc/include/eqc_c.hrl").
|
-include_lib("eqc/include/eqc_c.hrl").
|
||||||
|
@ -77,7 +77,8 @@ serial_command_gen(_Mod,#state{db=Db}=S) ->
|
||||||
{call,?IMPL,get,[Db,gen_key(S)]},
|
{call,?IMPL,get,[Db,gen_key(S)]},
|
||||||
{call,?IMPL,first,[Db]},
|
{call,?IMPL,first,[Db]},
|
||||||
{call,?IMPL,last,[Db]},
|
{call,?IMPL,last,[Db]},
|
||||||
{call,?IMPL,next,[Db,gen_key(S)]}
|
{call,?IMPL,next,[Db,gen_key(S)]},
|
||||||
|
{call,?IMPL,prev,[Db,gen_key(S)]}
|
||||||
]).
|
]).
|
||||||
|
|
||||||
parallel_command_gen(_Mod,#state{db=undefined}) ->
|
parallel_command_gen(_Mod,#state{db=undefined}) ->
|
||||||
|
@ -168,32 +169,46 @@ postcondition(S, {call,_,next,[_Db,Key]}, Res) ->
|
||||||
[#obj{key=K}|_] ->
|
[#obj{key=K}|_] ->
|
||||||
Res =:= K
|
Res =:= K
|
||||||
end;
|
end;
|
||||||
|
postcondition(S, {call,_,prev,[_Db,Key]}, Res) ->
|
||||||
|
case lists:dropwhile(fun(#obj{key=X}) -> X >= Key end, rsort_objs(S)) of
|
||||||
|
[] ->
|
||||||
|
Res;
|
||||||
|
[#obj{key=K}|_] ->
|
||||||
|
Res =:= K
|
||||||
|
end;
|
||||||
postcondition(_S, {call,_,_,_}, _Res) ->
|
postcondition(_S, {call,_,_,_}, _Res) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
-spec commands_setup(boolean()) -> {ok, term()}.
|
-spec setup(boolean()) -> {ok, term()}.
|
||||||
commands_setup(_Hard) ->
|
setup(_Hard) ->
|
||||||
?IMPL:setup(),
|
?IMPL:setup(),
|
||||||
teardown(),
|
teardown(),
|
||||||
{ok, unused}.
|
{ok, unused}.
|
||||||
|
|
||||||
-spec commands_teardown(term()) -> ok.
|
-spec teardown(term()) -> ok.
|
||||||
commands_teardown(unused) ->
|
teardown(unused) ->
|
||||||
teardown(),
|
teardown(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec commands_teardown(term(), #state{}) -> ok.
|
-spec teardown(term(), #state{}) -> ok.
|
||||||
commands_teardown(Ref, _State) ->
|
teardown(Ref, _State) ->
|
||||||
commands_teardown(Ref).
|
teardown(Ref).
|
||||||
|
|
||||||
|
-spec aggregate([{integer(), term(), term(), #state{}}])
|
||||||
|
-> [{atom(), atom(), integer() | term()}].
|
||||||
|
aggregate(L) ->
|
||||||
|
[ {Cmd,filter_reply(Reply)} || {_N,{set,_,{call,_,Cmd,_}},Reply,_State} <- L ].
|
||||||
|
|
||||||
|
filter_reply({'EXIT',{Err,_}}) ->
|
||||||
|
{error,Err};
|
||||||
|
filter_reply(_) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Internal
|
%%% Internal - Generators
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
teardown() ->
|
|
||||||
?IMPL:teardown().
|
|
||||||
|
|
||||||
gen_bytes() ->
|
gen_bytes() ->
|
||||||
?LET(B, list(choose(0,127)), list_to_binary(B)).
|
?LET(B, list(choose(0,127)), list_to_binary(B)).
|
||||||
|
|
||||||
|
@ -216,6 +231,11 @@ gen_obj(#state{objs=[]}) ->
|
||||||
gen_obj(#state{objs=Objs}) ->
|
gen_obj(#state{objs=Objs}) ->
|
||||||
oneof([oneof(Objs), gen_obj()]).
|
oneof([oneof(Objs), gen_obj()]).
|
||||||
|
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% Internal - Model
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
insert_obj(S, #obj{key=K}=Obj) ->
|
insert_obj(S, #obj{key=K}=Obj) ->
|
||||||
case keymember(K, S) of
|
case keymember(K, S) of
|
||||||
false ->
|
false ->
|
||||||
|
@ -238,6 +258,9 @@ get_val(S, K) ->
|
||||||
sort_objs(#state{objs=Objs}) ->
|
sort_objs(#state{objs=Objs}) ->
|
||||||
lists:sort(Objs).
|
lists:sort(Objs).
|
||||||
|
|
||||||
|
rsort_objs(S) ->
|
||||||
|
lists:reverse(sort_objs(S)).
|
||||||
|
|
||||||
keydelete(X, #state{objs=L}) ->
|
keydelete(X, #state{objs=L}) ->
|
||||||
lists:filter(fun(#obj{key=K}) -> K =/= X end, L).
|
lists:filter(fun(#obj{key=K}) -> K =/= X end, L).
|
||||||
|
|
||||||
|
@ -250,5 +273,14 @@ keyfind(X, #state{objs=L}) ->
|
||||||
keymember(X, S) ->
|
keymember(X, S) ->
|
||||||
[] /= keyfind(X, S).
|
[] /= keyfind(X, S).
|
||||||
|
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% Internal - Implementation
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
teardown() ->
|
||||||
|
?IMPL:teardown().
|
||||||
|
|
||||||
|
|
||||||
-endif. %% -ifdef(EQC).
|
-endif. %% -ifdef(EQC).
|
||||||
-endif. %% -ifdef(QC).
|
-endif. %% -ifdef(QC).
|
||||||
|
|
Loading…
Reference in a new issue