https://github.com/akkartik/mu/blob/main/301array-equal.subx
  1 # Comparing arrays of numbers.
  2 
  3 == code
  4 
  5 array-equal?:  # a: (addr array int), b: (addr array int) -> result/eax: boolean
  6     # pseudocode:
  7     #   asize = a->size
  8     #   if (asize != b->size) return false
  9     #   i = 0
 10     #   curra = a->data
 11     #   currb = b->data
 12     #   while i < asize
 13     #     i1 = *curra
 14     #     i2 = *currb
 15     #     if (c1 != c2) return false
 16     #     i+=4, curra+=4, currb+=4
 17     #   return true
 18     #
 19     # registers:
 20     #   i: ecx
 21     #   asize: edx
 22     #   curra: esi
 23     #   currb: edi
 24     #   i1: eax
 25     #   i2: ebx
 26     #
 27     # . prologue
 28     55/push-ebp
 29     89/<- %ebp 4/r32/esp
 30     # . save registers
 31     51/push-ecx
 32     52/push-edx
 33     53/push-ebx
 34     56/push-esi
 35     57/push-edi
 36     # esi = a
 37     8b/-> *(ebp+8) 6/r32/esi
 38     # edi = b
 39     8b/-> *(ebp+0xc) 7/r32/edi
 40     # var asize/edx: int = a->size
 41     8b/-> *esi 2/r32/edx
 42 $array-equal?:sizes:
 43     # if (asize != b->size) return false
 44     39/compare *edi 2/r32/edx
 45     75/jump-if-!= $array-equal?:false/disp8
 46     # var curra/esi: (addr byte) = a->data
 47     81 0/subop/add %esi 4/imm32
 48     # var currb/edi: (addr byte) = b->data
 49     81 0/subop/add %edi 4/imm32
 50     # var i/ecx: int = 0
 51     31/xor-with %ecx 1/r32/ecx
 52     # var vala/eax: int
 53     # var valb/ebx: int
 54 $array-equal?:loop:
 55     # if (i >= asize) return true
 56     39/compare %ecx 2/r32/edx
 57     7d/jump-if->= $array-equal?:true/disp8
 58     # var vala/eax: int = *curra
 59     8b/-> *esi 0/r32/eax
 60     # var valb/ebx: int = *currb
 61     8b/-> *edi 3/r32/ebx
 62     # if (vala != valb) return false
 63     39/compare %eax 3/r32/ebx
 64     75/jump-if-!= $array-equal?:false/disp8
 65     # i += 4
 66     81 0/subop/add %ecx 4/imm32
 67     # currs += 4
 68     81 0/subop/add %esi 4/imm32
 69     # currb += 4
 70     81 0/subop/add %edi 4/imm32
 71     eb/jump $array-equal?:loop/disp8
 72 $array-equal?:true:
 73     b8/copy-to-eax 1/imm32
 74     eb/jump $array-equal?:end/disp8
 75 $array-equal?:false:
 76     b8/copy-to-eax 0/imm32
 77 $array-equal?:end:
 78     # . restore registers
 79     5f/pop-to-edi
 80     5e/pop-to-esi
 81     5b/pop-to-ebx
 82     5a/pop-to-edx
 83     59/pop-to-ecx
 84     # . epilogue
 85     89/<- %esp 5/r32/ebp
 86     5d/pop-to-ebp
 87     c3/return
 88 
 89 test-compare-empty-with-empty-array:
 90     # . prologue
 91     55/push-ebp
 92     89/<- %ebp 4/r32/esp
 93     # var ecx: (array _) = []
 94     68/push 0/imm32/size
 95     89/<- %ecx 4/r32/esp
 96     # var edx: (array _) = []
 97     68/push 0/imm32/size
 98     89/<- %edx 4/r32/esp
 99     #
100     (array-equal? %ecx %edx)  # => eax
101     (check-ints-equal %eax 1 "F - test-compare-empty-with-empty-array")
102     # . epilogue
103     89/<- %esp 5/r32/ebp
104     5d/pop-to-ebp
105     c3/return
106 
107 test-compare-empty-with-non-empty-array:  # also checks size-mismatch code path
108     # . prologue
109     55/push-ebp
110     89/<- %ebp 4/r32/esp
111     # var ecx: (array int) = [1]
112     68/push 1/imm32
113     68/push 4/imm32/size
114     89/<- %ecx 4/r32/esp
115     # var edx: (array int) = []
116     68/push 0/imm32/size
117     89/<- %edx 4/r32/esp
118     #
119     (array-equal? %ecx %edx)  # => eax
120     (check-ints-equal %eax 0 "F - test-compare-empty-with-non-empty-array")
121     # . epilogue
122     89/<- %esp 5/r32/ebp
123     5d/pop-to-ebp
124     c3/return
125 
126 test-compare-equal-arrays:
127     # . prologue
128     55/push-ebp
129     89/<- %ebp 4/r32/esp
130     # var ecx: (array int) = [1, 2, 3]
131     68/push 3/imm32
132     68/push 2/imm32
133     68/push 1/imm32
134     68/push 0xc/imm32/size
135     89/<- %ecx 4/r32/esp
136     # var edx: (array int) = [1, 2, 3]
137     68/push 3/imm32
138     68/push 2/imm32
139     68/push 1/imm32
140     68/push 0xc/imm32/size
141     89/<- %edx 4/r32/esp
142     #
143     (array-equal? %ecx %edx)  # => eax
144     (check-ints-equal %eax 1 "F - test-compare-equal-arrays")
145     # . epilogue
146     89/<- %esp 5/r32/ebp
147     5d/pop-to-ebp
148     c3/return
149 
150 test-compare-inequal-arrays-equal-sizes:
151     # . prologue
152     55/push-ebp
153     89/<- %ebp 4/r32/esp
154     # var ecx: (array int) = [1, 4, 3]
155     68/push 3/imm32
156     68/push 4/imm32
157     68/push 1/imm32
158     68/push 0xc/imm32/size
159     89/<- %ecx 4/r32/esp
160     # var edx: (array int) = [1, 2, 3]
161     68/push 3/imm32
162     68/push 2/imm32
163     68/push 1/imm32
164     68/push 0xc/imm32/size
165     89/<- %edx 4/r32/esp
166     #
167     (array-equal? %ecx %edx)  # => eax
168     (check-ints-equal %eax 0 "F - test-compare-inequal-arrays-equal-sizes")
169     # . epilogue
170     89/<- %esp 5/r32/ebp
171     5d/pop-to-ebp
172     c3/return
173 
174 _parse-array-of-ints:  # ad: (addr allocation-descriptor), s: (addr array byte), out: (addr handle array int)
175     # pseudocode
176     #   end = &s->data[s->size]
177     #   curr = s->data
178     #   size = 0
179     #   while true
180     #     if (curr >= end) break
181     #     curr = skip-chars-matching-in-slice(curr, end, ' ')
182     #     if (curr >= end) break
183     #     curr = skip-chars-not-matching-in-slice(curr, end, ' ')
184     #     ++size
185     #   allocate-array(ad, size*4, out)
186     #   var slice: slice = {s->data, 0}
187     #   curr = lookup(out)->data
188     #   while true
189     #     if (slice->start >= end) break
190     #     slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
191     #     if (slice->start >= end) break
192     #     slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
193     #     *curr = parse-hex-int-from-slice(slice)
194     #     curr += 4
195     #     slice->start = slice->end
196     #   return result
197     #
198     # . prologue
199     55/push-ebp
200     89/<- %ebp 4/r32/esp
201     # . save registers
202     50/push-eax
203     51/push-ecx
204     52/push-edx
205     53/push-ebx
206     56/push-esi
207     57/push-edi
208     # esi = s
209     8b/-> *(ebp+0xc) 6/r32/esi
210     # var curr/ecx: (addr byte) = s->data
211     8d/copy-address *(esi+4) 1/r32/ecx
212     # var end/edx: (addr byte) = &s->data[s->size]
213     # . edx = s->size
214     8b/-> *esi 2/r32/edx
215     # . edx += curr
216     01/add-to %edx 1/r32/ecx
217     # var size/ebx: int = 0
218     31/xor-with %ebx 3/r32/ebx
219 $_parse-array-of-ints:loop1:
220     # if (curr >= end) break
221     39/compare %ecx 2/r32/edx
222     73/jump-if-addr>= $_parse-array-of-ints:break1/disp8
223     # curr = skip-chars-matching-in-slice(curr, end, ' ')
224     (skip-chars-matching-in-slice %ecx %edx 0x20)  # => eax
225     89/<- %ecx 0/r32/eax
226     # if (curr >= end) break
227     39/compare %ecx 2/r32/edx
228     73/jump-if-addr>= $_parse-array-of-ints:break1/disp8
229     # curr = skip-chars-not-matching-in-slice(curr, end, ' ')
230     (skip-chars-not-matching-in-slice %ecx %edx 0x20)  # => eax
231     89/<- %ecx 0/r32/eax
232     # size += 4
233     81 0/subop/add %ebx 4/imm32
234     eb/jump $_parse-array-of-ints:loop1/disp8
235 $_parse-array-of-ints:break1:
236     (allocate-array *(ebp+8) %ebx *(ebp+0x10))
237 $_parse-array-of-ints:pass2:
238     # var slice/edi: slice = {s->data, 0}
239     68/push 0/imm32/end
240     8d/copy-address *(esi+4) 7/r32/edi
241     57/push-edi
242     89/<- %edi 4/r32/esp
243     # curr = lookup(out)->data
244     8b/-> *(ebp+0x10) 0/r32/eax
245     (lookup *eax *(eax+4))  # => eax
246     8d/copy-address *(eax+4) 1/r32/ecx
247 $_parse-array-of-ints:loop2:
248     # if (slice->start >= end) break
249     39/compare *edi 2/r32/edgt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $
** Lua tables (hash)
** See Copyright Notice in lua.h
*/


/*
** Implementation of tables (aka arrays, objects, or hash tables).
** Tables keep its elements in two parts: an array part and a hash part.
** Non-negative integer keys are all candidates to be kept in the array
** part. The actual size of the array is the largest `n' such that at
** least half the slots between 0 and n are in use.
** Hash uses a mix of chained scatter table with Brent's variation.
** A main invariant of these tables is that, if an element is not
** in its main position (i.e. the `original' position that its hash gives
** to it), then the colliding element is in its own main position.
** Hence even when the load factor reaches 100%, performance remains good.
*/

#include <math.h>
#include <string.h>

#define ltable_c
#define LUA_CORE

#include "lua.h"

#include "ldebug.h"
#include "ldo.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "ltable.h"


/*
** max size of array part is 2^MAXBITS
*/
#if LUAI_BITSINT > 26
#define MAXBITS		26
#else
#define MAXBITS		(LUAI_BITSINT-2)
#endif

#define MAXASIZE	(1 << MAXBITS)


#define hashpow2(t,n)      (gnode(t, lmod((n), sizenode(t))))
  
#define hashstr(t,str)  hashpow2(t, (str)->tsv.hash)
#define hashboolean(t,p)        hashpow2(t, p)


/*
** for some types, it is better to avoid modulus by power of 2, as
** they tend to have many 2 factors.
*/
#define hashmod(t,n)	(gnode(t, ((n) % ((sizenode(t)-1)|1))))


#define hashpointer(t,p)	hashmod(t, IntPoint(p))


/*
** number of ints inside a lua_Number
*/
#define numints		cast_int(sizeof(lua_Number)/sizeof(int))



#define dummynode		(&dummynode_)

static const Node dummynode_ = {
  {{NULL}, LUA_TNIL},  /* value */
  {{{NULL}, LUA_TNIL, NULL}}  /* key */
};


/*
** hash for lua_Numbers
*/
static Node *hashnum (const Table *t, lua_Number n) {
  unsigned int a[numints];
  int i;
  if (luai_numeq(n, 0))  /* avoid problems with -0 */
    return gnode(t, 0);
  memcpy(a, &n, sizeof(a));
  for (i = 1; i < numints; i++) a[0] += a[i];
  return hashmod(t, a[0]);
}



/*
** returns the `main' position of an element in a table (that is, the index
** of its hash value)
*/
static Node *mainposition (const Table *t, const TValue *key) {
  switch (ttype(key)) {
    case LUA_TNUMBER:
      return hashnum(t, nvalue(key));
    case LUA_TSTRING:
      return hashstr(t, rawtsvalue(key));
    case LUA_TBOOLEAN:
      return hashboolean(t, bvalue(key));
    case LUA_TLIGHTUSERDATA:
      return hashpointer(t, pvalue(key));
    default:
      return hashpointer(t, gcvalue(key));
  }
}


/*
** returns the index for `key' if `key' is an appropriate key to live in
** the array part of the table, -1 otherwise.
*/
static int arrayindex (const TValue *key) {
  if (ttisnumber(key)) {
    lua_Number n = nvalue(key);
    int k;
    lua_number2int(k, n);
    if (luai_numeq(cast_num(k), n))
      return k;
  }
  return -1