small_array.odin 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. package container_small_array
  2. import "base:builtin"
  3. import "base:runtime"
  4. _ :: 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(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(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.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 new length will be:
  195. - `length` if `length` <= capacity
  196. - capacity if length > capacity
  197. **Inputs**
  198. - `a`: A pointer to the small-array
  199. - `length`: The new desired length
  200. Example:
  201. import "core:container/small_array"
  202. import "core:fmt"
  203. resize_example :: proc() {
  204. a: small_array.Small_Array(5, int)
  205. small_array.push_back(&a, 1)
  206. small_array.push_back(&a, 2)
  207. fmt.println(small_array.slice(&a))
  208. small_array.resize(&a, 1)
  209. fmt.println(small_array.slice(&a))
  210. small_array.resize(&a, 100)
  211. fmt.println(small_array.slice(&a))
  212. }
  213. Output:
  214. [1, 2]
  215. [1]
  216. [1, 2, 0, 0, 0]
  217. */
  218. resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
  219. a.len = min(length, builtin.len(a.data))
  220. }
  221. /*
  222. Attempts to add the given element to the end.
  223. **Inputs**
  224. - `a`: A pointer to the small-array
  225. - `item`: The item to append
  226. **Returns**
  227. - true if there was enough space to fit the element, false otherwise
  228. Example:
  229. import "core:container/small_array"
  230. import "core:fmt"
  231. push_back_example :: proc() {
  232. a: small_array.Small_Array(2, int)
  233. assert(small_array.push_back(&a, 1), "this should fit")
  234. assert(small_array.push_back(&a, 2), "this should fit")
  235. assert(!small_array.push_back(&a, 3), "this should not fit")
  236. fmt.println(small_array.slice(&a))
  237. }
  238. Output:
  239. [1, 2]
  240. */
  241. push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
  242. if a.len < cap(a^) {
  243. a.data[a.len] = item
  244. a.len += 1
  245. return true
  246. }
  247. return false
  248. }
  249. /*
  250. Attempts to add the given element at the beginning.
  251. This operation assumes that the small-array is not empty.
  252. Note: Performing this operation will cause pointers obtained
  253. through get_ptr(_save) to reference incorrect elements.
  254. **Inputs**
  255. - `a`: A pointer to the small-array
  256. - `item`: The item to append
  257. **Returns**
  258. - true if there was enough space to fit the element, false otherwise
  259. Example:
  260. import "core:container/small_array"
  261. import "core:fmt"
  262. push_front_example :: proc() {
  263. a: small_array.Small_Array(2, int)
  264. assert(small_array.push_front(&a, 2), "this should fit")
  265. assert(small_array.push_front(&a, 1), "this should fit")
  266. assert(!small_array.push_back(&a, 0), "this should not fit")
  267. fmt.println(small_array.slice(&a))
  268. }
  269. Output:
  270. [1, 2]
  271. */
  272. push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
  273. if a.len < cap(a^) {
  274. a.len += 1
  275. data := slice(a)
  276. copy(data[1:], data[:])
  277. data[0] = item
  278. return true
  279. }
  280. return false
  281. }
  282. /*
  283. Removes and returns the last element of the small-array.
  284. This operation assumes that the small-array is not empty.
  285. **Inputs**
  286. - `a`: A pointer to the small-array
  287. **Returns**
  288. - a copy of the element removed from the end of the small-array
  289. Example:
  290. import "core:container/small_array"
  291. import "core:fmt"
  292. pop_back_example :: proc() {
  293. a: small_array.Small_Array(5, int)
  294. small_array.push(&a, 0, 1, 2)
  295. fmt.println("BEFORE:", small_array.slice(&a))
  296. small_array.pop_back(&a)
  297. fmt.println("AFTER: ", small_array.slice(&a))
  298. }
  299. Output:
  300. BEFORE: [0, 1, 2]
  301. AFTER: [0, 1]
  302. */
  303. pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
  304. assert(condition=(N > 0 && a.len > 0), loc=loc)
  305. item := a.data[a.len-1]
  306. a.len -= 1
  307. return item
  308. }
  309. /*
  310. Removes and returns the first element of the small-array.
  311. This operation assumes that the small-array is not empty.
  312. Note: Performing this operation will cause pointers obtained
  313. through get_ptr(_save) to reference incorrect elements.
  314. **Inputs**
  315. - `a`: A pointer to the small-array
  316. **Returns**
  317. - a copy of the element removed from the beginning of the small-array
  318. Example:
  319. import "core:container/small_array"
  320. import "core:fmt"
  321. pop_front_example :: proc() {
  322. a: small_array.Small_Array(5, int)
  323. small_array.push(&a, 0, 1, 2)
  324. fmt.println("BEFORE:", small_array.slice(&a))
  325. small_array.pop_front(&a)
  326. fmt.println("AFTER: ", small_array.slice(&a))
  327. }
  328. Output:
  329. BEFORE: [0, 1, 2]
  330. AFTER: [1, 2]
  331. */
  332. pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
  333. assert(condition=(N > 0 && a.len > 0), loc=loc)
  334. item := a.data[0]
  335. s := slice(a)
  336. copy(s[:], s[1:])
  337. a.len -= 1
  338. return item
  339. }
  340. /*
  341. Attempts to remove and return the last element of the small array.
  342. Unlike `pop_back`, it does not assume that the array is non-empty.
  343. **Inputs**
  344. - `a`: A pointer to the small-array
  345. **Returns**
  346. - a copy of the element removed from the end of the small-array
  347. - true if the small-array was not empty, false otherwise
  348. Example:
  349. import "core:container/small_array"
  350. pop_back_safe_example :: proc() {
  351. a: small_array.Small_Array(3, int)
  352. small_array.push(&a, 1)
  353. el, ok := small_array.pop_back_safe(&a)
  354. assert(ok, "there was an element in the array")
  355. el, ok = small_array.pop_back_safe(&a)
  356. assert(!ok, "there was NO element in the array")
  357. }
  358. */
  359. pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
  360. if N > 0 && a.len > 0 {
  361. item = a.data[a.len-1]
  362. a.len -= 1
  363. ok = true
  364. }
  365. return
  366. }
  367. /*
  368. Attempts to remove and return the first element of the small array.
  369. Unlike `pop_front`, it does not assume that the array is non-empty.
  370. Note: Performing this operation will cause pointers obtained
  371. through get_ptr(_save) to reference incorrect elements.
  372. **Inputs**
  373. - `a`: A pointer to the small-array
  374. **Returns**
  375. - a copy of the element removed from the beginning of the small-array
  376. - true if the small-array was not empty, false otherwise
  377. Example:
  378. import "core:container/small_array"
  379. pop_front_safe_example :: proc() {
  380. a: small_array.Small_Array(3, int)
  381. small_array.push(&a, 1)
  382. el, ok := small_array.pop_front_safe(&a)
  383. assert(ok, "there was an element in the array")
  384. el, ok = small_array.pop_front_(&a)
  385. assert(!ok, "there was NO element in the array")
  386. }
  387. */
  388. pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
  389. if N > 0 && a.len > 0 {
  390. item = a.data[0]
  391. s := slice(a)
  392. copy(s[:], s[1:])
  393. a.len -= 1
  394. ok = true
  395. }
  396. return
  397. }
  398. /*
  399. Decreases the length of the small-array by the given amount.
  400. The elements are therefore not really removed and can be
  401. recovered by calling `resize`.
  402. Note: This procedure assumes that the array has a sufficient length.
  403. **Inputs**
  404. - `a`: A pointer to the small-array
  405. - `count`: The amount the length should be reduced by
  406. Example:
  407. import "core:container/small_array"
  408. import "core:fmt"
  409. consume_example :: proc() {
  410. a: small_array.Small_Array(3, int)
  411. small_array.push(&a, 0, 1, 2)
  412. fmt.println("BEFORE:", small_array.slice(&a))
  413. small_array.consume(&a, 2)
  414. fmt.println("AFTER :", small_array.slice(&a))
  415. }
  416. Output:
  417. BEFORE: [0, 1, 2]
  418. AFTER : [0]
  419. */
  420. consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
  421. assert(condition=a.len >= count, loc=loc)
  422. a.len -= count
  423. }
  424. /*
  425. Removes the element at the specified index while retaining order.
  426. Note: Performing this operation will cause pointers obtained
  427. through get_ptr(_save) to reference incorrect elements.
  428. **Inputs**
  429. - `a`: A pointer to the small-array
  430. - `index`: The position of the element to remove
  431. Example:
  432. import "core:container/small_array"
  433. import "core:fmt"
  434. ordered_remove_example :: proc() {
  435. a: small_array.Small_Array(4, int)
  436. small_array.push(&a, 0, 1, 2, 3)
  437. fmt.println("BEFORE:", small_array.slice(&a))
  438. small_array.ordered_remove(&a, 1)
  439. fmt.println("AFTER :", small_array.slice(&a))
  440. }
  441. Output:
  442. BEFORE: [0, 1, 2, 3]
  443. AFTER : [0, 2, 3]
  444. */
  445. ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
  446. runtime.bounds_check_error_loc(loc, index, a.len)
  447. if index+1 < a.len {
  448. copy(a.data[index:], a.data[index+1:])
  449. }
  450. a.len -= 1
  451. }
  452. /*
  453. Removes the element at the specified index without retaining order.
  454. **Inputs**
  455. - `a`: A pointer to the small-array
  456. - `index`: The position of the element to remove
  457. Example:
  458. import "core:container/small_array"
  459. import "core:fmt"
  460. unordered_remove_example :: proc() {
  461. a: small_array.Small_Array(4, int)
  462. small_array.push(&a, 0, 1, 2, 3)
  463. fmt.println("BEFORE:", small_array.slice(&a))
  464. small_array.unordered_remove(&a, 1)
  465. fmt.println("AFTER :", small_array.slice(&a))
  466. }
  467. Output:
  468. BEFORE: [0, 1, 2, 3]
  469. AFTER : [0, 3, 2]
  470. */
  471. unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
  472. runtime.bounds_check_error_loc(loc, index, a.len)
  473. n := a.len-1
  474. if index != n {
  475. a.data[index] = a.data[n]
  476. }
  477. a.len -= 1
  478. }
  479. /*
  480. Sets the length of the small-array to 0.
  481. **Inputs**
  482. - `a`: A pointer to the small-array
  483. Example:
  484. import "core:container/small_array"
  485. import "core:fmt"
  486. clear_example :: proc() {
  487. a: small_array.Small_Array(4, int)
  488. small_array.push(&a, 0, 1, 2, 3)
  489. fmt.println("BEFORE:", small_array.slice(&a))
  490. small_array.clear(&a)
  491. fmt.println("AFTER :", small_array.slice(&a))
  492. }
  493. Output:
  494. BEFORE: [0, 1, 2, 3]
  495. AFTER : []
  496. */
  497. clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
  498. resize(a, 0)
  499. }
  500. /*
  501. Attempts to append all elements to the small-array returning
  502. false if there is not enough space to fit all of them.
  503. **Inputs**
  504. - `a`: A pointer to the small-array
  505. - `item`: The item to append
  506. - ..:
  507. **Returns**
  508. - true if there was enough space to fit the element, false otherwise
  509. Example:
  510. import "core:container/small_array"
  511. import "core:fmt"
  512. push_back_elems_example :: proc() {
  513. a: small_array.Small_Array(100, int)
  514. small_array.push_back_elems(&a, 0, 1, 2, 3, 4)
  515. fmt.println(small_array.slice(&a))
  516. }
  517. Output:
  518. [0, 1, 2, 3, 4]
  519. */
  520. push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -> bool {
  521. if a.len + builtin.len(items) <= cap(a^) {
  522. n := copy(a.data[a.len:], items[:])
  523. a.len += n
  524. return true
  525. }
  526. return false
  527. }
  528. /*
  529. Tries to insert an element at the specified position.
  530. Note: Performing this operation will cause pointers obtained
  531. through get_ptr(_save) to reference incorrect elements.
  532. **Inputs**
  533. - `a`: A pointer to the small-array
  534. - `item`: The item to insert
  535. - `index`: The index to insert the item at
  536. **Returns**
  537. - true if there was enough space to fit the element, false otherwise
  538. Example:
  539. import "core:container/small_array"
  540. import "core:fmt"
  541. inject_at_example :: proc() {
  542. arr: small_array.Small_Array(100, rune)
  543. small_array.push(&arr, 'A', 'C', 'D')
  544. small_array.inject_at(&arr, 'B', 1)
  545. fmt.println(small_array.slice(&arr))
  546. }
  547. Output:
  548. [A, B, C, D]
  549. */
  550. inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
  551. if a.len < cap(a^) && index >= 0 && index <= len(a^) {
  552. a.len += 1
  553. for i := a.len - 1; i >= index + 1; i -= 1 {
  554. a.data[i] = a.data[i - 1]
  555. }
  556. a.data[index] = item
  557. return true
  558. }
  559. return false
  560. }
  561. // Alias for `push_back`
  562. append_elem :: push_back
  563. // Alias for `push_back_elems`
  564. append_elems :: push_back_elems
  565. /*
  566. Tries to append the element(s) to the small-array.
  567. **Inputs**
  568. - `a`: A pointer to the small-array
  569. - `item`: The item to append
  570. - ..:
  571. **Returns**
  572. - true if there was enough space to fit the element, false otherwise
  573. Example:
  574. import "core:container/small_array"
  575. import "core:fmt"
  576. push_example :: proc() {
  577. a: small_array.Small_Array(100, int)
  578. small_array.push(&a, 0)
  579. small_array.push(&a, 1, 2, 3, 4)
  580. fmt.println(small_array.slice(&a))
  581. }
  582. Output:
  583. [0, 1, 2, 3, 4]
  584. */
  585. push :: proc{push_back, push_back_elems}
  586. // Alias for `push`
  587. append :: proc{push_back, push_back_elems}