small_array.odin 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. package container_small_array
  2. import "base:builtin"
  3. @require import "base:intrinsics"
  4. @require import "base:runtime"
  5. /*
  6. A fixed-size stack-allocated array operated on in a dynamic fashion.
  7. Fields:
  8. - `data`: The underlying array
  9. - `len`: Amount of items that the `Small_Array` currently holds
  10. Example:
  11. import "core:container/small_array"
  12. example :: proc() {
  13. a: small_array.Small_Array(100, int)
  14. small_array.push_back(&a, 10)
  15. }
  16. */
  17. Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
  18. data: [N]T,
  19. len: int,
  20. }
  21. /*
  22. Returns the amount of items in the small-array.
  23. **Inputs**
  24. - `a`: The small-array
  25. **Returns**
  26. - the amount of items in the array
  27. */
  28. len :: proc "contextless" (a: $A/Small_Array) -> int {
  29. return a.len
  30. }
  31. /*
  32. Returns the capacity of the small-array.
  33. **Inputs**
  34. - `a`: The small-array
  35. **Returns** the capacity
  36. */
  37. cap :: proc "contextless" (a: $A/Small_Array) -> int {
  38. return builtin.len(a.data)
  39. }
  40. /*
  41. Returns how many more items the small-array could fit.
  42. **Inputs**
  43. - `a`: The small-array
  44. **Returns**
  45. - the number of unused slots
  46. */
  47. space :: proc "contextless" (a: $A/Small_Array) -> int {
  48. return builtin.len(a.data) - a.len
  49. }
  50. /*
  51. Returns a slice of the data.
  52. **Inputs**
  53. - `a`: The pointer to the small-array
  54. **Returns**
  55. - the slice
  56. Example:
  57. import "core:container/small_array"
  58. import "core:fmt"
  59. slice_example :: proc() {
  60. print :: proc(a: ^small_array.Small_Array($N, int)) {
  61. for item in small_array.slice(a) {
  62. fmt.println(item)
  63. }
  64. }
  65. a: small_array.Small_Array(5, int)
  66. small_array.push_back(&a, 1)
  67. small_array.push_back(&a, 2)
  68. print(&a)
  69. }
  70. Output:
  71. 1
  72. 2
  73. */
  74. slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
  75. return a.data[:a.len]
  76. }
  77. /*
  78. Get a copy of the item at the specified position.
  79. This operation assumes that the small-array is large enough.
  80. This will result in:
  81. - the value if 0 <= index < len
  82. - the zero value of the type if len < index < capacity
  83. - 'crash' if capacity < index or index < 0
  84. **Inputs**
  85. - `a`: The small-array
  86. - `index`: The position of the item to get
  87. **Returns**
  88. - the element at the specified position
  89. */
  90. get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
  91. return a.data[index]
  92. }
  93. /*
  94. Get a pointer to the item at the specified position.
  95. This operation assumes that the small-array is large enough.
  96. This will result in:
  97. - the pointer if 0 <= index < len
  98. - the pointer to the zero value if len < index < capacity
  99. - 'crash' if capacity < index or index < 0
  100. **Inputs**
  101. - `a`: A pointer to the small-array
  102. - `index`: The position of the item to get
  103. **Returns**
  104. - the pointer to the element at the specified position
  105. */
  106. get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
  107. return &a.data[index]
  108. }
  109. /*
  110. Attempt to get a copy of the item at the specified position.
  111. **Inputs**
  112. - `a`: The small-array
  113. - `index`: The position of the item to get
  114. **Returns**
  115. - the element at the specified position
  116. - true if element exists, false otherwise
  117. Example:
  118. import "core:container/small_array"
  119. import "core:fmt"
  120. get_safe_example :: proc() {
  121. a: small_array.Small_Array(5, rune)
  122. small_array.push_back(&a, 'A')
  123. fmt.println(small_array.get_safe(a, 0) or_else 'x')
  124. fmt.println(small_array.get_safe(a, 1) or_else 'x')
  125. }
  126. Output:
  127. A
  128. x
  129. */
  130. get_safe :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
  131. if index < 0 || index >= a.len {
  132. return {}, false
  133. }
  134. return a.data[index], true
  135. }
  136. /*
  137. Get a pointer to the item at the specified position.
  138. **Inputs**
  139. - `a`: A pointer to the small-array
  140. - `index`: The position of the item to get
  141. **Returns**
  142. - the pointer to the element at the specified position
  143. - true if element exists, false otherwise
  144. */
  145. get_ptr_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
  146. if index < 0 || index >= a.len {
  147. return {}, false
  148. }
  149. return &a.data[index], true
  150. }
  151. /*
  152. Set the element at the specified position to the given value.
  153. This operation assumes that the small-array is large enough.
  154. This will result in:
  155. - the value being set if 0 <= index < capacity
  156. - 'crash' otherwise
  157. **Inputs**
  158. - `a`: A pointer to the small-array
  159. - `index`: The position of the item to set
  160. - `value`: The value to set the element to
  161. Example:
  162. import "core:container/small_array"
  163. import "core:fmt"
  164. set_example :: proc() {
  165. a: small_array.Small_Array(5, rune)
  166. small_array.push_back(&a, 'A')
  167. small_array.push_back(&a, 'B')
  168. fmt.println(small_array.slice(&a))
  169. // updates index 0
  170. small_array.set(&a, 0, 'Z')
  171. fmt.println(small_array.slice(&a))
  172. // updates to a position x, where
  173. // len <= x < cap are not visible since
  174. // the length of the small-array remains unchanged
  175. small_array.set(&a, 2, 'X')
  176. small_array.set(&a, 3, 'Y')
  177. small_array.set(&a, 4, 'Z')
  178. fmt.println(small_array.slice(&a))
  179. // resizing makes the change visible
  180. small_array.non_zero_resize(&a, 100)
  181. fmt.println(small_array.slice(&a))
  182. }
  183. Output:
  184. [A, B]
  185. [Z, B]
  186. [Z, B]
  187. [Z, B, X, Y, Z]
  188. */
  189. set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
  190. a.data[index] = item
  191. }
  192. /*
  193. Tries to resize the small-array to the specified length.
  194. The memory of added elements will be zeroed out.
  195. The new length will be:
  196. - `length` if `length` <= capacity
  197. - capacity if length > capacity
  198. **Inputs**
  199. - `a`: A pointer to the small-array
  200. - `length`: The new desired length
  201. Example:
  202. import "core:container/small_array"
  203. import "core:fmt"
  204. resize_example :: proc() {
  205. a: small_array.Small_Array(5, int)
  206. small_array.push_back(&a, 1)
  207. small_array.push_back(&a, 2)
  208. fmt.println(small_array.slice(&a))
  209. small_array.resize(&a, 1)
  210. fmt.println(small_array.slice(&a))
  211. small_array.resize(&a, 100)
  212. fmt.println(small_array.slice(&a))
  213. }
  214. Output:
  215. [1, 2]
  216. [1]
  217. [1, 0, 0, 0, 0]
  218. */
  219. resize :: proc "contextless" (a: ^$A/Small_Array($N, $T), length: int) {
  220. prev_len := a.len
  221. a.len = min(length, builtin.len(a.data))
  222. if prev_len < a.len {
  223. intrinsics.mem_zero(&a.data[prev_len], size_of(T)*(a.len-prev_len))
  224. }
  225. }
  226. /*
  227. Tries to resize the small-array to the specified length.
  228. The new length will be:
  229. - `length` if `length` <= capacity
  230. - capacity if length > capacity
  231. **Inputs**
  232. - `a`: A pointer to the small-array
  233. - `length`: The new desired length
  234. Example:
  235. import "core:container/small_array"
  236. import "core:fmt"
  237. non_zero_resize :: proc() {
  238. a: small_array.Small_Array(5, int)
  239. small_array.push_back(&a, 1)
  240. small_array.push_back(&a, 2)
  241. fmt.println(small_array.slice(&a))
  242. small_array.non_zero_resize(&a, 1)
  243. fmt.println(small_array.slice(&a))
  244. small_array.non_zero_resize(&a, 100)
  245. fmt.println(small_array.slice(&a))
  246. }
  247. Output:
  248. [1, 2]
  249. [1]
  250. [1, 2, 0, 0, 0]
  251. */
  252. non_zero_resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
  253. a.len = min(length, builtin.len(a.data))
  254. }
  255. /*
  256. Attempts to add the given element to the end.
  257. **Inputs**
  258. - `a`: A pointer to the small-array
  259. - `item`: The item to append
  260. **Returns**
  261. - true if there was enough space to fit the element, false otherwise
  262. Example:
  263. import "core:container/small_array"
  264. import "core:fmt"
  265. push_back_example :: proc() {
  266. a: small_array.Small_Array(2, int)
  267. assert(small_array.push_back(&a, 1), "this should fit")
  268. assert(small_array.push_back(&a, 2), "this should fit")
  269. assert(!small_array.push_back(&a, 3), "this should not fit")
  270. fmt.println(small_array.slice(&a))
  271. }
  272. Output:
  273. [1, 2]
  274. */
  275. push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
  276. if a.len < cap(a^) {
  277. a.data[a.len] = item
  278. a.len += 1
  279. return true
  280. }
  281. return false
  282. }
  283. /*
  284. Attempts to add the given element at the beginning.
  285. This operation assumes that the small-array is not empty.
  286. Note: Performing this operation will cause pointers obtained
  287. through get_ptr(_save) to reference incorrect elements.
  288. **Inputs**
  289. - `a`: A pointer to the small-array
  290. - `item`: The item to append
  291. **Returns**
  292. - true if there was enough space to fit the element, false otherwise
  293. Example:
  294. import "core:container/small_array"
  295. import "core:fmt"
  296. push_front_example :: proc() {
  297. a: small_array.Small_Array(2, int)
  298. assert(small_array.push_front(&a, 2), "this should fit")
  299. assert(small_array.push_front(&a, 1), "this should fit")
  300. assert(!small_array.push_back(&a, 0), "this should not fit")
  301. fmt.println(small_array.slice(&a))
  302. }
  303. Output:
  304. [1, 2]
  305. */
  306. push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
  307. if a.len < cap(a^) {
  308. a.len += 1
  309. data := slice(a)
  310. copy(data[1:], data[:])
  311. data[0] = item
  312. return true
  313. }
  314. return false
  315. }
  316. /*
  317. Removes and returns the last element of the small-array.
  318. This operation assumes that the small-array is not empty.
  319. **Inputs**
  320. - `a`: A pointer to the small-array
  321. **Returns**
  322. - a copy of the element removed from the end of the small-array
  323. Example:
  324. import "core:container/small_array"
  325. import "core:fmt"
  326. pop_back_example :: proc() {
  327. a: small_array.Small_Array(5, int)
  328. small_array.push(&a, 0, 1, 2)
  329. fmt.println("BEFORE:", small_array.slice(&a))
  330. small_array.pop_back(&a)
  331. fmt.println("AFTER: ", small_array.slice(&a))
  332. }
  333. Output:
  334. BEFORE: [0, 1, 2]
  335. AFTER: [0, 1]
  336. */
  337. pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
  338. assert(condition=(N > 0 && a.len > 0), loc=loc)
  339. item := a.data[a.len-1]
  340. a.len -= 1
  341. return item
  342. }
  343. /*
  344. Removes and returns the first element of the small-array.
  345. This operation assumes that the small-array is not empty.
  346. Note: Performing this operation will cause pointers obtained
  347. through get_ptr(_save) to reference incorrect elements.
  348. **Inputs**
  349. - `a`: A pointer to the small-array
  350. **Returns**
  351. - a copy of the element removed from the beginning of the small-array
  352. Example:
  353. import "core:container/small_array"
  354. import "core:fmt"
  355. pop_front_example :: proc() {
  356. a: small_array.Small_Array(5, int)
  357. small_array.push(&a, 0, 1, 2)
  358. fmt.println("BEFORE:", small_array.slice(&a))
  359. small_array.pop_front(&a)
  360. fmt.println("AFTER: ", small_array.slice(&a))
  361. }
  362. Output:
  363. BEFORE: [0, 1, 2]
  364. AFTER: [1, 2]
  365. */
  366. pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
  367. assert(condition=(N > 0 && a.len > 0), loc=loc)
  368. item := a.data[0]
  369. s := slice(a)
  370. copy(s[:], s[1:])
  371. a.len -= 1
  372. return item
  373. }
  374. /*
  375. Attempts to remove and return the last element of the small array.
  376. Unlike `pop_back`, it does not assume that the array is non-empty.
  377. **Inputs**
  378. - `a`: A pointer to the small-array
  379. **Returns**
  380. - a copy of the element removed from the end of the small-array
  381. - true if the small-array was not empty, false otherwise
  382. Example:
  383. import "core:container/small_array"
  384. pop_back_safe_example :: proc() {
  385. a: small_array.Small_Array(3, int)
  386. small_array.push(&a, 1)
  387. el, ok := small_array.pop_back_safe(&a)
  388. assert(ok, "there was an element in the array")
  389. el, ok = small_array.pop_back_safe(&a)
  390. assert(!ok, "there was NO element in the array")
  391. }
  392. */
  393. pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
  394. if N > 0 && a.len > 0 {
  395. item = a.data[a.len-1]
  396. a.len -= 1
  397. ok = true
  398. }
  399. return
  400. }
  401. /*
  402. Attempts to remove and return the first element of the small array.
  403. Unlike `pop_front`, it does not assume that the array is non-empty.
  404. Note: Performing this operation will cause pointers obtained
  405. through get_ptr(_save) to reference incorrect elements.
  406. **Inputs**
  407. - `a`: A pointer to the small-array
  408. **Returns**
  409. - a copy of the element removed from the beginning of the small-array
  410. - true if the small-array was not empty, false otherwise
  411. Example:
  412. import "core:container/small_array"
  413. pop_front_safe_example :: proc() {
  414. a: small_array.Small_Array(3, int)
  415. small_array.push(&a, 1)
  416. el, ok := small_array.pop_front_safe(&a)
  417. assert(ok, "there was an element in the array")
  418. el, ok = small_array.pop_front_(&a)
  419. assert(!ok, "there was NO element in the array")
  420. }
  421. */
  422. pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
  423. if N > 0 && a.len > 0 {
  424. item = a.data[0]
  425. s := slice(a)
  426. copy(s[:], s[1:])
  427. a.len -= 1
  428. ok = true
  429. }
  430. return
  431. }
  432. /*
  433. Decreases the length of the small-array by the given amount.
  434. The elements are therefore not really removed and can be
  435. recovered by calling `resize`.
  436. Note: This procedure assumes that the array has a sufficient length.
  437. **Inputs**
  438. - `a`: A pointer to the small-array
  439. - `count`: The amount the length should be reduced by
  440. Example:
  441. import "core:container/small_array"
  442. import "core:fmt"
  443. consume_example :: proc() {
  444. a: small_array.Small_Array(3, int)
  445. small_array.push(&a, 0, 1, 2)
  446. fmt.println("BEFORE:", small_array.slice(&a))
  447. small_array.consume(&a, 2)
  448. fmt.println("AFTER :", small_array.slice(&a))
  449. }
  450. Output:
  451. BEFORE: [0, 1, 2]
  452. AFTER : [0]
  453. */
  454. consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
  455. assert(condition=a.len >= count, loc=loc)
  456. a.len -= count
  457. }
  458. /*
  459. Removes the element at the specified index while retaining order.
  460. Note: Performing this operation will cause pointers obtained
  461. through get_ptr(_save) to reference incorrect elements.
  462. **Inputs**
  463. - `a`: A pointer to the small-array
  464. - `index`: The position of the element to remove
  465. Example:
  466. import "core:container/small_array"
  467. import "core:fmt"
  468. ordered_remove_example :: proc() {
  469. a: small_array.Small_Array(4, int)
  470. small_array.push(&a, 0, 1, 2, 3)
  471. fmt.println("BEFORE:", small_array.slice(&a))
  472. small_array.ordered_remove(&a, 1)
  473. fmt.println("AFTER :", small_array.slice(&a))
  474. }
  475. Output:
  476. BEFORE: [0, 1, 2, 3]
  477. AFTER : [0, 2, 3]
  478. */
  479. ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
  480. runtime.bounds_check_error_loc(loc, index, a.len)
  481. if index+1 < a.len {
  482. copy(a.data[index:], a.data[index+1:])
  483. }
  484. a.len -= 1
  485. }
  486. /*
  487. Removes the element at the specified index without retaining order.
  488. **Inputs**
  489. - `a`: A pointer to the small-array
  490. - `index`: The position of the element to remove
  491. Example:
  492. import "core:container/small_array"
  493. import "core:fmt"
  494. unordered_remove_example :: proc() {
  495. a: small_array.Small_Array(4, int)
  496. small_array.push(&a, 0, 1, 2, 3)
  497. fmt.println("BEFORE:", small_array.slice(&a))
  498. small_array.unordered_remove(&a, 1)
  499. fmt.println("AFTER :", small_array.slice(&a))
  500. }
  501. Output:
  502. BEFORE: [0, 1, 2, 3]
  503. AFTER : [0, 3, 2]
  504. */
  505. unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
  506. runtime.bounds_check_error_loc(loc, index, a.len)
  507. n := a.len-1
  508. if index != n {
  509. a.data[index] = a.data[n]
  510. }
  511. a.len -= 1
  512. }
  513. /*
  514. Sets the length of the small-array to 0.
  515. **Inputs**
  516. - `a`: A pointer to the small-array
  517. Example:
  518. import "core:container/small_array"
  519. import "core:fmt"
  520. clear_example :: proc() {
  521. a: small_array.Small_Array(4, int)
  522. small_array.push(&a, 0, 1, 2, 3)
  523. fmt.println("BEFORE:", small_array.slice(&a))
  524. small_array.clear(&a)
  525. fmt.println("AFTER :", small_array.slice(&a))
  526. }
  527. Output:
  528. BEFORE: [0, 1, 2, 3]
  529. AFTER : []
  530. */
  531. clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
  532. resize(a, 0)
  533. }
  534. /*
  535. Attempts to append all elements to the small-array returning
  536. false if there is not enough space to fit all of them.
  537. **Inputs**
  538. - `a`: A pointer to the small-array
  539. - `item`: The item to append
  540. - ..:
  541. **Returns**
  542. - true if there was enough space to fit the element, false otherwise
  543. Example:
  544. import "core:container/small_array"
  545. import "core:fmt"
  546. push_back_elems_example :: proc() {
  547. a: small_array.Small_Array(100, int)
  548. small_array.push_back_elems(&a, 0, 1, 2, 3, 4)
  549. fmt.println(small_array.slice(&a))
  550. }
  551. Output:
  552. [0, 1, 2, 3, 4]
  553. */
  554. push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -> bool {
  555. if a.len + builtin.len(items) <= cap(a^) {
  556. n := copy(a.data[a.len:], items[:])
  557. a.len += n
  558. return true
  559. }
  560. return false
  561. }
  562. /*
  563. Tries to insert an element at the specified position.
  564. Note: Performing this operation will cause pointers obtained
  565. through get_ptr(_save) to reference incorrect elements.
  566. **Inputs**
  567. - `a`: A pointer to the small-array
  568. - `item`: The item to insert
  569. - `index`: The index to insert the item at
  570. **Returns**
  571. - true if there was enough space to fit the element, false otherwise
  572. Example:
  573. import "core:container/small_array"
  574. import "core:fmt"
  575. inject_at_example :: proc() {
  576. arr: small_array.Small_Array(100, rune)
  577. small_array.push(&arr, 'A', 'C', 'D')
  578. small_array.inject_at(&arr, 'B', 1)
  579. fmt.println(small_array.slice(&arr))
  580. }
  581. Output:
  582. [A, B, C, D]
  583. */
  584. inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
  585. if a.len < cap(a^) && index >= 0 && index <= len(a^) {
  586. a.len += 1
  587. for i := a.len - 1; i >= index + 1; i -= 1 {
  588. a.data[i] = a.data[i - 1]
  589. }
  590. a.data[index] = item
  591. return true
  592. }
  593. return false
  594. }
  595. // Alias for `push_back`
  596. append_elem :: push_back
  597. // Alias for `push_back_elems`
  598. append_elems :: push_back_elems
  599. /*
  600. Tries to append the element(s) to the small-array.
  601. **Inputs**
  602. - `a`: A pointer to the small-array
  603. - `item`: The item to append
  604. - ..:
  605. **Returns**
  606. - true if there was enough space to fit the element, false otherwise
  607. Example:
  608. import "core:container/small_array"
  609. import "core:fmt"
  610. push_example :: proc() {
  611. a: small_array.Small_Array(100, int)
  612. small_array.push(&a, 0)
  613. small_array.push(&a, 1, 2, 3, 4)
  614. fmt.println(small_array.slice(&a))
  615. }
  616. Output:
  617. [0, 1, 2, 3, 4]
  618. */
  619. push :: proc{push_back, push_back_elems}
  620. // Alias for `push`
  621. append :: proc{push_back, push_back_elems}