├── .appveyor.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── ranges.nim ├── ranges.nimble ├── ranges ├── bitranges.nim ├── memranges.nim ├── ptr_arith.nim ├── stackarrays.nim └── typedranges.nim └── tests ├── all.nim ├── tbitranges.nim ├── tstackarrays.nim └── ttypedranges.nim /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | image: Visual Studio 2015 4 | 5 | cache: 6 | - NimBinaries 7 | 8 | matrix: 9 | # We always want 32 and 64-bit compilation 10 | fast_finish: false 11 | 12 | platform: 13 | - x86 14 | - x64 15 | 16 | # when multiple CI builds are queued, the tested commit needs to be in the last X commits cloned with "--depth X" 17 | clone_depth: 10 18 | 19 | install: 20 | # use the newest versions documented here: https://www.appveyor.com/docs/windows-images-software/#mingw-msys-cygwin 21 | - IF "%PLATFORM%" == "x86" SET PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH% 22 | - IF "%PLATFORM%" == "x64" SET PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;%PATH% 23 | 24 | # build nim from our own branch - this to avoid the day-to-day churn and 25 | # regressions of the fast-paced Nim development while maintaining the 26 | # flexibility to apply patches 27 | - curl -O -L -s -S https://raw.githubusercontent.com/status-im/nimbus/devel/build_nim.sh 28 | - env MAKE="mingw32-make -j2" ARCH_OVERRIDE=%PLATFORM% bash build_nim.sh Nim csources dist/nimble NimBinaries 29 | - SET PATH=%CD%\Nim\bin;%PATH% 30 | 31 | build_script: 32 | - cd C:\projects\%APPVEYOR_PROJECT_SLUG% 33 | - nimble install -y 34 | 35 | test_script: 36 | - nimble test 37 | 38 | deploy: off 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore all executable files 2 | * 3 | !*.* 4 | !*/ 5 | 6 | nimcache/ 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | # https://docs.travis-ci.com/user/caching/ 4 | cache: 5 | directories: 6 | - NimBinaries 7 | 8 | git: 9 | # when multiple CI builds are queued, the tested commit needs to be in the last X commits cloned with "--depth X" 10 | depth: 10 11 | 12 | os: 13 | - linux 14 | - osx 15 | 16 | install: 17 | # build nim from our own branch - this to avoid the day-to-day churn and 18 | # regressions of the fast-paced Nim development while maintaining the 19 | # flexibility to apply patches 20 | - curl -O -L -s -S https://raw.githubusercontent.com/status-im/nimbus/devel/build_nim.sh 21 | - env MAKE="make -j2" bash build_nim.sh Nim csources dist/nimble NimBinaries 22 | - export PATH=$PWD/Nim/bin:$PATH 23 | 24 | script: 25 | - nimble install -y 26 | - nimble test 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Status Research & Development GmbH 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ranges 2 | 3 | **Deprecated - moved to https://github.com/status-im/nim-stew** 4 | 5 | [![Build Status (Travis)](https://img.shields.io/travis/status-im/nimbus/master.svg?label=Linux%20/%20macOS "Linux/macOS build status (Travis)")](https://travis-ci.org/status-im/nimbus) 6 | [![Windows build status (Appveyor)](https://img.shields.io/appveyor/ci/nimbus/nimbus/master.svg?label=Windows "Windows build status (Appveyor)")](https://ci.appveyor.com/project/nimbus/nimbus) 7 | [![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 8 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 9 | ![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg) 10 | 11 | # Introduction 12 | This library implements utility functions for working with parts and blobs of memory. 13 | 14 | # Installation 15 | $ nimble install ranges 16 | 17 | 18 | ## License 19 | 20 | Licensed and distributed under either of 21 | 22 | * MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT 23 | 24 | or 25 | 26 | * Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0) 27 | 28 | at your option. This file may not be copied, modified, or distributed except according to those terms. 29 | -------------------------------------------------------------------------------- /ranges.nim: -------------------------------------------------------------------------------- 1 | import 2 | ranges/memranges, 3 | ranges/typedranges 4 | 5 | export 6 | memranges, typedranges 7 | -------------------------------------------------------------------------------- /ranges.nimble: -------------------------------------------------------------------------------- 1 | mode = ScriptMode.Verbose 2 | 3 | packageName = "ranges" 4 | version = "0.0.1" 5 | author = "Status Research & Development GmbH" 6 | description = "Exploration of various implementations of memory range types" 7 | license = "Apache License 2.0" 8 | skipDirs = @["tests"] 9 | 10 | requires "nim >= 0.17.0" 11 | 12 | proc configForTests() = 13 | --hints: off 14 | --debuginfo 15 | --path: "." 16 | --run 17 | --threads: on 18 | 19 | task test, "run CPU tests": 20 | configForTests() 21 | setCommand "c", "tests/all.nim" 22 | 23 | -------------------------------------------------------------------------------- /ranges/bitranges.nim: -------------------------------------------------------------------------------- 1 | import 2 | typedranges, ptr_arith 3 | 4 | type 5 | BitRange* = object 6 | data: MutByteRange 7 | start: int 8 | mLen: int 9 | 10 | BitIndexable = SomeUnsignedInt 11 | 12 | template `@`(s, idx: untyped): untyped = 13 | (when idx is BackwardsIndex: s.len - int(idx) else: int(idx)) 14 | 15 | proc bits*(a: MutByteRange, start, len: int): BitRange = 16 | doAssert start <= len 17 | doAssert len <= 8 * a.len 18 | result.data = a 19 | result.start = start 20 | result.mLen = len 21 | 22 | template bits*(a: var seq[byte], start, len: int): BitRange = 23 | bits(a.toRange, start, len) 24 | 25 | template bits*(a: MutByteRange): BitRange = 26 | bits(a, 0, a.len * 8) 27 | 28 | template bits*(a: var seq[byte]): BitRange = 29 | bits(a.toRange, 0, a.len * 8) 30 | 31 | template bits*(a: MutByteRange, len: int): BitRange = 32 | bits(a, 0, len) 33 | 34 | template bits*(a: var seq[byte], len: int): BitRange = 35 | bits(a.toRange, 0, len) 36 | 37 | template bits*(bytes: MutByteRange, slice: HSlice): BitRange = 38 | bits(bytes, bytes @ slice.a, bytes @ slice.b) 39 | 40 | template bits*(x: BitRange): BitRange = x 41 | 42 | template mostSignificantBit(T: typedesc): auto = 43 | const res = 1 shl (sizeof(T) * 8 - 1) 44 | T(res) 45 | 46 | template getBit*(x: BitIndexable, bit: Natural): bool = 47 | ## reads a bit from `x`, assuming 0 to be the position of the 48 | ## most significant bit 49 | (x and mostSignificantBit(x.type) shr bit) != 0 50 | 51 | template getBitLE*(x: BitIndexable, bit: Natural): bool = 52 | ## reads a bit from `x`, assuming 0 to be the position of the 53 | ## least significant bit 54 | type T = type(x) 55 | (x and T(0b1 shl bit)) != 0 56 | 57 | proc setBit*(x: var BitIndexable, bit: Natural, val: bool) = 58 | ## writes a bit in `x`, assuming 0 to be the position of the 59 | ## most significant bit 60 | let mask = mostSignificantBit(x.type) shr bit 61 | if val: 62 | x = x or mask 63 | else: 64 | x = x and not mask 65 | 66 | proc setBitLE*(x: var BitIndexable, bit: Natural, val: bool) = 67 | ## writes a bit in `x`, assuming 0 to be the position of the 68 | ## least significant bit 69 | type T = type(x) 70 | let mask = 0b1 shl bit 71 | if val: 72 | x = x or mask 73 | else: 74 | x = x and not mask 75 | 76 | proc raiseBit*(x: var BitIndexable, bit: Natural) = 77 | ## raises a bit in `x`, assuming 0 to be the position of the 78 | ## most significant bit 79 | type T = type(x) 80 | let mask = mostSignificantBit(x.type) shr bit 81 | x = x or mask 82 | 83 | proc lowerBit*(x: var BitIndexable, bit: Natural) = 84 | ## raises a bit in `x`, assuming 0 to be the position of the 85 | ## most significant bit 86 | type T = type(x) 87 | let mask = mostSignificantBit(x.type) shr bit 88 | x = x and not mask 89 | 90 | proc raiseBitLE*(x: var BitIndexable, bit: Natural) = 91 | ## raises bit in `x`, assuming 0 to be the position of the 92 | ## least significant bit 93 | type T = type(x) 94 | let mask = 0b1 shl bit 95 | x = x or mask 96 | 97 | proc lowerBitLE*(x: var BitIndexable, bit: Natural) = 98 | ## raises bit in a byte, assuming 0 to be the position of the 99 | ## least significant bit 100 | type T = type(x) 101 | let mask = 0b1 shl bit 102 | x = x and not mask 103 | 104 | proc len*(r: BitRange): int {.inline.} = r.mLen 105 | 106 | template getAbsoluteBit(bytes, absIdx: untyped): bool = 107 | ## Returns a bit with a position relative to the start of 108 | ## the underlying range. Not to be confused with a position 109 | ## relative to the start of the BitRange (i.e. the two would 110 | ## match only when range.start == 0). 111 | let 112 | byteToCheck = absIdx shr 3 # the same as absIdx / 8 113 | bitToCheck = (absIdx and 0b111) 114 | 115 | getBit(bytes[byteToCheck], bitToCheck) 116 | 117 | template setAbsoluteBit(bytes, absIdx, value) = 118 | let 119 | byteToWrite = absIdx shr 3 # the same as absIdx / 8 120 | bitToWrite = (absIdx and 0b111) 121 | 122 | setBit(bytes[byteToWrite], bitToWrite, value) 123 | 124 | iterator enumerateBits(x: BitRange): (int, bool) = 125 | var p = x.start 126 | var i = 0 127 | let e = x.len 128 | while i != e: 129 | yield (i, getAbsoluteBit(x.data, p)) 130 | inc p 131 | inc i 132 | 133 | proc getBit*(bytes: openarray[byte], pos: Natural): bool = 134 | getAbsoluteBit(bytes, pos) 135 | 136 | proc setBit*(bytes: var openarray[byte], pos: Natural, value: bool) = 137 | setAbsoluteBit(bytes, pos, value) 138 | 139 | iterator items*(x: BitRange): bool = 140 | for _, v in enumerateBits(x): yield v 141 | 142 | iterator pairs*(x: BitRange): (int, bool) = 143 | for i, v in enumerateBits(x): yield (i, v) 144 | 145 | proc `[]`*(x: BitRange, idx: int): bool {.inline.} = 146 | doAssert idx < x.len 147 | let p = x.start + idx 148 | result = getAbsoluteBit(x.data, p) 149 | 150 | proc sliceNormalized(x: BitRange, ibegin, iend: int): BitRange = 151 | doAssert ibegin >= 0 and 152 | ibegin < x.len and 153 | iend < x.len and 154 | iend + 1 >= ibegin # the +1 here allows the result to be 155 | # an empty range 156 | 157 | result.data = x.data 158 | result.start = x.start + ibegin 159 | result.mLen = iend - ibegin + 1 160 | 161 | proc `[]`*(r: BitRange, s: HSlice): BitRange {.inline.} = 162 | sliceNormalized(r, r @ s.a, r @ s.b) 163 | 164 | proc `==`*(a, b: BitRange): bool = 165 | if a.len != b.len: return false 166 | for i in 0 ..< a.len: 167 | if a[i] != b[i]: return false 168 | true 169 | 170 | proc `[]=`*(r: var BitRange, idx: Natural, val: bool) {.inline.} = 171 | doAssert idx < r.len 172 | let absIdx = r.start + idx 173 | setAbsoluteBit(r.data, absIdx, val) 174 | 175 | proc setAbsoluteBit(x: BitRange, absIdx: int, val: bool) {.inline.} = 176 | ## Assumes the destination bit is already zeroed. 177 | ## Works with absolute positions similar to `getAbsoluteBit` 178 | doAssert absIdx < x.len 179 | let 180 | byteToWrite = absIdx shr 3 # the same as absIdx / 8 181 | bitToWrite = (absIdx and 0b111) 182 | 183 | if val: 184 | raiseBit x.data[byteToWrite], bitToWrite 185 | 186 | proc pushFront*(x: var BitRange, val: bool) = 187 | doAssert x.start > 0 188 | dec x.start 189 | x[0] = val 190 | inc x.mLen 191 | 192 | template neededBytes(nBits: int): int = 193 | (nBits shr 3) + ord((nBits and 0b111) != 0) 194 | 195 | static: 196 | doAssert neededBytes(2) == 1 197 | doAssert neededBytes(8) == 1 198 | doAssert neededBytes(9) == 2 199 | 200 | proc `&`*(a, b: BitRange): BitRange = 201 | let totalLen = a.len + b.len 202 | 203 | var bytes = newSeq[byte](totalLen.neededBytes) 204 | result = bits(bytes, 0, totalLen) 205 | 206 | for i in 0 ..< a.len: result.setAbsoluteBit(i, a[i]) 207 | for i in 0 ..< b.len: result.setAbsoluteBit(i + a.len, b[i]) 208 | 209 | proc `$`*(r: BitRange): string = 210 | result = newStringOfCap(r.len) 211 | for b in r: 212 | result.add(if b: '1' else: '0') 213 | 214 | proc fromBits*(T: typedesc, r: BitRange, offset, num: Natural): T = 215 | doAssert(num <= sizeof(T) * 8) 216 | # XXX: Nim has a bug that a typedesc parameter cannot be used 217 | # in a type coercion, so we must define an alias here: 218 | type TT = T 219 | for i in 0 ..< num: 220 | result = (result shl 1) or TT(r[offset + i]) 221 | 222 | proc parse*(T: typedesc[BitRange], s: string): BitRange = 223 | var bytes = newSeq[byte](s.len.neededBytes) 224 | for i, c in s: 225 | case c 226 | of '0': discard 227 | of '1': raiseBit(bytes[i shr 3], i and 0b111) 228 | else: doAssert false 229 | result = bits(bytes, 0, s.len) 230 | 231 | -------------------------------------------------------------------------------- /ranges/memranges.nim: -------------------------------------------------------------------------------- 1 | import 2 | ptr_arith 3 | 4 | type 5 | MemRange* = object 6 | start: pointer 7 | size: csize 8 | 9 | template len*(mr: MemRange): int = mr.size 10 | template `[]`*(mr: MemRange, idx: int): byte = (cast[ptr byte](shift(mr.start, idx)))[] 11 | proc baseAddr*(mr: MemRange): pointer = mr.start 12 | 13 | proc makeMemRange*(start: pointer, size: csize): MemRange = 14 | result.start = start 15 | result.size = size 16 | 17 | proc toMemRange*(x: string): MemRange = 18 | result.start = x.cstring.pointer 19 | result.size = x.len 20 | 21 | proc toMemRange*[T](x: openarray[T], fromIdx, toIdx: int): MemRange = 22 | doAssert(fromIdx >= 0 and toIdx >= fromIdx and fromIdx < x.len and toIdx < x.len) 23 | result.start = unsafeAddr x[fromIdx] 24 | result.size = (toIdx - fromIdx + 1) * T.sizeof 25 | 26 | proc toMemRange*[T](x: openarray[T], fromIdx: int): MemRange {.inline.} = 27 | toMemRange(x, fromIdx, x.high) 28 | 29 | proc toMemRange*[T](x: openarray[T]): MemRange {.inline.} = 30 | toMemRange(x, 0, x.high) 31 | 32 | template toMemRange*(mr: MemRange): MemRange = mr 33 | -------------------------------------------------------------------------------- /ranges/ptr_arith.nim: -------------------------------------------------------------------------------- 1 | proc baseAddr*[T](x: openarray[T]): pointer = cast[pointer](x) 2 | 3 | proc shift*(p: pointer, delta: int): pointer {.inline.} = 4 | cast[pointer](cast[int](p) + delta) 5 | 6 | proc distance*(a, b: pointer): int {.inline.} = 7 | cast[int](b) - cast[int](a) 8 | 9 | proc shift*[T](p: ptr T, delta: int): ptr T {.inline.} = 10 | cast[ptr T](shift(cast[pointer](p), delta * sizeof(T))) 11 | 12 | when (NimMajor,NimMinor,NimPatch) >= (0,19,9): 13 | template makeOpenArray*[T](p: ptr T, len: int): auto = 14 | toOpenArray(cast[ptr UncheckedArray[T]](p), 0, len - 1) 15 | 16 | template makeOpenArray*(p: pointer, T: type, len: int): auto = 17 | toOpenArray(cast[ptr UncheckedArray[T]](p), 0, len - 1) 18 | 19 | else: 20 | # TODO: These are fallbacks until we upgrade to 0.19.9 21 | template makeOpenArray*(p: pointer, T: type, len: int): auto = 22 | toOpenArray(cast[ptr array[0, T]](p)[], 0, len - 1) 23 | 24 | template makeOpenArray*[T](p: ptr T, len: int): auto = 25 | toOpenArray(cast[ptr array[0, T]](p)[], 0, len - 1) 26 | 27 | -------------------------------------------------------------------------------- /ranges/stackarrays.nim: -------------------------------------------------------------------------------- 1 | ## Stack-allocated arrays should be used with great care. 2 | ## 3 | ## They pose several major risks: 4 | ## 5 | ## 1. They should not be used inside resumable procs 6 | ## (i.e. closure iterators and async procs) 7 | ## 8 | ## Future versions of the library may automatically 9 | ## detect such usages and flag them as errors 10 | ## 11 | ## 2. The user code should be certain that enough stack space 12 | ## is available for the allocation and there will be enough 13 | ## room for additional calls after the allocation. 14 | ## 15 | ## Future versions of this library may provide checks 16 | ## 17 | ## Please note that the stack size on certain platforms 18 | ## may be very small (e.g. 8 to 32 kb on some Android versions) 19 | ## 20 | ## Before using alloca-backed arrays, consider using: 21 | ## 22 | ## 1. A regular stack array with a reasonable size 23 | ## 24 | ## 2. A global {.threadvar.} sequence that can be resized when 25 | ## needed (only in non-reentrant procs) 26 | ## 27 | ## Other possible future directions: 28 | ## 29 | ## Instead of `alloca`, we may start using a shadow stack that will be much 30 | ## harder to overflow. This will work by allocating a very large chunk of the 31 | ## address space at program init (e.g. 1TB on a 64-bit system) and then by 32 | ## gradually committing the individual pages to memory as they are requested. 33 | ## 34 | ## Such a scheme will even allow us to resize the stack array on demand 35 | ## in situations where the final size is not known upfront. With a resizing 36 | ## factor of 2, we'll never waste more than 50% of the memory which should 37 | ## be reasonable for short-lived allocations. 38 | ## 39 | 40 | type 41 | StackArray*[T] = object 42 | bufferLen: int32 43 | buffer: ptr UncheckedArray[T] 44 | 45 | when defined(windows): 46 | proc alloca(n: int): pointer {.importc, header: "".} 47 | else: 48 | proc alloca(n: int): pointer {.importc, header: "".} 49 | 50 | proc raiseRangeError(s: string) = 51 | raise newException(RangeError, s) 52 | 53 | proc raiseOutOfRange = 54 | raiseRangeError "index out of range" 55 | 56 | template len*(a: StackArray): int = 57 | int(a.bufferLen) 58 | 59 | template high*(a: StackArray): int = 60 | int(a.bufferLen) - 1 61 | 62 | template low*(a: StackArray): int = 63 | 0 64 | 65 | template `[]`*(a: StackArray, i: int): auto = 66 | if i < 0 or i >= a.len: raiseOutOfRange() 67 | a.buffer[i] 68 | 69 | proc `[]=`*(a: StackArray, i: int, val: a.T) {.inline.} = 70 | if i < 0 or i >= a.len: raiseOutOfRange() 71 | a.buffer[i] = val 72 | 73 | template `[]`*(a: StackArray, i: BackwardsIndex): auto = 74 | if int(i) < 1 or int(i) > a.len: raiseOutOfRange() 75 | a.buffer[a.len - int(i)] 76 | 77 | proc `[]=`*(a: StackArray, i: BackwardsIndex, val: a.T) = 78 | if int(i) < 1 or int(i) > a.len: raiseOutOfRange() 79 | a.buffer[a.len - int(i)] = val 80 | 81 | iterator items*(a: StackArray): a.T = 82 | for i in 0 .. a.high: 83 | yield a.buffer[i] 84 | 85 | iterator mitems*(a: var StackArray): var a.T = 86 | for i in 0 .. a.high: 87 | yield a.buffer[i] 88 | 89 | iterator pairs*(a: StackArray): a.T = 90 | for i in 0 .. a.high: 91 | yield (i, a.buffer[i]) 92 | 93 | iterator mpairs*(a: var StackArray): (int, var a.T) = 94 | for i in 0 .. a.high: 95 | yield (i, a.buffer[i]) 96 | 97 | template allocaAux(sz: int, init: static[bool]): pointer = 98 | let s = sz 99 | let b = alloca(s) 100 | when init: zeroMem(b, s) 101 | b 102 | 103 | template allocStackArrayAux(T: typedesc, size: int, init: static[bool]): StackArray[T] = 104 | let sz = int(size) # Evaluate size only once 105 | if sz < 0: raiseRangeError "allocation with a negative size" 106 | # XXX: is it possible to perform a stack size check before calling `alloca`? 107 | # On thread init, Nim may record the base address and the capacity of the stack, 108 | # so in theory we can verify that we still have enough room for the allocation. 109 | # Research this. 110 | StackArray[T](bufferLen: int32(sz), buffer: cast[ptr UncheckedArray[T]](allocaAux(sz * sizeof(T), init))) 111 | 112 | template allocStackArray*(T: typedesc, size: int): StackArray[T] = 113 | allocStackArrayAux(T, size, true) 114 | 115 | template allocStackArrayNoInit*(T: typedesc, size: int): StackArray[T] = 116 | allocStackArrayAux(T, size, false) 117 | 118 | template getBuffer*(a: StackArray): untyped = 119 | when (NimMajor,NimMinor,NimPatch)>=(0,19,9): 120 | a.buffer 121 | else: 122 | a.buffer[] 123 | 124 | template toOpenArray*(a: StackArray): auto = 125 | toOpenArray(a.getBuffer, 0, a.high) 126 | 127 | template toOpenArray*(a: StackArray, first: int): auto = 128 | if first < 0 or first >= a.len: raiseOutOfRange() 129 | toOpenArray(a.getBuffer, first, a.high) 130 | 131 | template toOpenArray*(a: StackArray, first, last: int): auto = 132 | if first < 0 or first >= last or last <= a.len: raiseOutOfRange() 133 | toOpenArray(a.getBuffer, first, last) 134 | -------------------------------------------------------------------------------- /ranges/typedranges.nim: -------------------------------------------------------------------------------- 1 | import ./ptr_arith, typetraits, hashes 2 | 3 | const rangesGCHoldEnabled = not defined(rangesDisableGCHold) 4 | const unsafeAPIEnabled* = defined(rangesEnableUnsafeAPI) 5 | 6 | type 7 | # A view into immutable array 8 | Range* {.shallow.} [T] = object 9 | when rangesGCHoldEnabled: 10 | gcHold: seq[T] 11 | start: ptr T 12 | mLen: int 13 | 14 | # A view into mutable array 15 | MutRange* {.shallow.} [T] = distinct Range[T] 16 | 17 | ByteRange* = Range[byte] 18 | MutByteRange* = MutRange[byte] 19 | 20 | proc isLiteral[T](s: seq[T]): bool {.inline.} = 21 | type 22 | SeqHeader = object 23 | length, reserved: int 24 | (cast[ptr SeqHeader](s).reserved and (1 shl (sizeof(int) * 8 - 2))) != 0 25 | 26 | proc toImmutableRange[T](a: seq[T]): Range[T] = 27 | if a.len != 0: 28 | when rangesGCHoldEnabled: 29 | if not isLiteral(a): 30 | shallowCopy(result.gcHold, a) 31 | else: 32 | result.gcHold = a 33 | result.start = addr result.gcHold[0] 34 | result.mLen = a.len 35 | 36 | when unsafeAPIEnabled: 37 | proc toImmutableRangeNoGCHold[T](a: openarray[T]): Range[T] = 38 | if a.len != 0: 39 | result.start = unsafeAddr a[0] 40 | result.mLen = a.len 41 | 42 | proc toImmutableRange[T](a: openarray[T]): Range[T] {.inline.} = 43 | toImmutableRangeNoGCHold(a) 44 | 45 | proc toRange*[T](a: var seq[T]): MutRange[T] {.inline.} = 46 | MutRange[T](toImmutableRange(a)) 47 | 48 | when unsafeAPIEnabled: 49 | proc toRange*[T](a: var openarray[T]): MutRange[T] {.inline.} = 50 | MutRange[T](toImmutableRange(a)) 51 | 52 | template initStackRange*[T](sz: static[int]): MutRange[T] = 53 | var data: array[sz, T] 54 | data.toRange() 55 | 56 | proc toRange*[T](a: openarray[T]): Range[T] {.inline.} = toImmutableRange(a) 57 | 58 | proc unsafeRangeConstruction*[T](a: var openarray[T]): MutRange[T] {.inline.} = 59 | MutRange[T](toImmutableRange(a)) 60 | 61 | proc unsafeRangeConstruction*[T](a: openarray[T]): Range[T] {.inline.} = 62 | toImmutableRange(a) 63 | 64 | proc newRange*[T](sz: int): MutRange[T] {.inline.} = 65 | MutRange[T](toImmutableRange(newSeq[T](sz))) 66 | 67 | proc toRange*[T](a: seq[T]): Range[T] {.inline.} = toImmutableRange(a) 68 | 69 | converter toImmutableRange*[T](a: MutRange[T]): Range[T] {.inline.} = Range[T](a) 70 | 71 | proc len*(r: Range): int {.inline.} = int(r.mLen) 72 | 73 | proc high*(r: Range): int {.inline.} = r.len - 1 74 | proc low*(r: Range): int {.inline.} = 0 75 | 76 | proc elemAt[T](r: MutRange[T], idx: int): var T {.inline.} = 77 | doAssert(idx < r.len) 78 | Range[T](r).start.shift(idx)[] 79 | 80 | proc `[]=`*[T](r: MutRange[T], idx: int, v: T) {.inline.} = r.elemAt(idx) = v 81 | proc `[]`*[T](r: MutRange[T], i: int): var T = r.elemAt(i) 82 | 83 | proc `[]`*[T](r: Range[T], idx: int): T {.inline.} = 84 | doAssert(idx < r.len) 85 | r.start.shift(idx)[] 86 | 87 | proc `==`*[T](a, b: Range[T]): bool = 88 | if a.len != b.len: return false 89 | equalMem(a.start, b.start, sizeof(T) * a.len) 90 | 91 | iterator ptrs[T](r: Range[T]): (int, ptr T) = 92 | var p = r.start 93 | var i = 0 94 | let e = r.len 95 | while i != e: 96 | yield (i, p) 97 | p = p.shift(1) 98 | inc i 99 | 100 | iterator items*[T](r: Range[T]): T = 101 | for _, v in ptrs(r): yield v[] 102 | 103 | iterator pairs*[T](r: Range[T]): (int, T) = 104 | for i, v in ptrs(r): yield (i, v[]) 105 | 106 | iterator mitems*[T](r: MutRange[T]): var T = 107 | for _, v in ptrs(r): yield v[] 108 | 109 | iterator mpairs*[T](r: MutRange[T]): (int, var T) = 110 | for i, v in ptrs(r): yield (i, v[]) 111 | 112 | proc toSeq*[T](r: Range[T]): seq[T] = 113 | result = newSeqOfCap[T](r.len) 114 | for i in r: result.add(i) 115 | 116 | proc `$`*(r: Range): string = 117 | result = "R[" 118 | for i, v in r: 119 | if i != 0: 120 | result &= ", " 121 | result &= $v 122 | result &= "]" 123 | 124 | proc sliceNormalized[T](r: Range[T], ibegin, iend: int): Range[T] = 125 | doAssert ibegin >= 0 and 126 | ibegin < r.len and 127 | iend < r.len and 128 | iend + 1 >= ibegin # the +1 here allows the result to be 129 | # an empty range 130 | 131 | when rangesGCHoldEnabled: 132 | shallowCopy(result.gcHold, r.gcHold) 133 | result.start = r.start.shift(ibegin) 134 | result.mLen = iend - ibegin + 1 135 | 136 | proc slice*[T](r: Range[T], ibegin = 0, iend = -1): Range[T] = 137 | let e = if iend < 0: r.len + iend 138 | else: iend 139 | sliceNormalized(r, ibegin, e) 140 | 141 | proc slice*[T](r: MutRange[T], ibegin = 0, iend = -1): MutRange[T] {.inline.} = 142 | MutRange[T](Range[T](r).slice(ibegin, iend)) 143 | 144 | template `^^`(s, i: untyped): untyped = 145 | (when i is BackwardsIndex: s.len - int(i) else: int(i)) 146 | 147 | proc `[]`*[T, U, V](r: Range[T], s: HSlice[U, V]): Range[T] {.inline.} = 148 | sliceNormalized(r, r ^^ s.a, r ^^ s.b) 149 | 150 | proc `[]`*[T, U, V](r: MutRange[T], s: HSlice[U, V]): MutRange[T] {.inline.} = 151 | MutRange[T](sliceNormalized(r, r ^^ s.a, r ^^ s.b)) 152 | 153 | proc `[]=`*[T, U, V](r: MutRange[T], s: HSlice[U, V], v: openarray[T]) = 154 | let a = r ^^ s.a 155 | let b = r ^^ s.b 156 | let L = b - a + 1 157 | if L == v.len: 158 | for i in 0..=(0,19,9): 165 | # error message in Nim HEAD 2019-01-02: 166 | # "for a 'var' type a variable needs to be passed, but 'toOpenArray(cast[ptr UncheckedArray[T]](curHash.start), 0, high(curHash))' is immutable" 167 | toOpenArray(cast[ptr UncheckedArray[T]](r.start), 0, r.high) 168 | else: 169 | # NOTE: `0` in `array[0, T]` is irrelevant 170 | toOpenArray(cast[ptr array[0, T]](r.start)[], 0, r.high) 171 | 172 | proc `[]=`*[T, U, V](r: MutRange[T], s: HSlice[U, V], v: Range[T]) {.inline.} = 173 | r[s] = toOpenArray(v) 174 | 175 | proc baseAddr*[T](r: Range[T]): ptr T {.inline.} = r.start 176 | proc gcHolder*[T](r: Range[T]): ptr T {.inline.} = 177 | ## This procedure is used only for shallow test, do not use it 178 | ## in production. 179 | when rangesGCHoldEnabled: 180 | if r.len > 0: 181 | result = unsafeAddr r.gcHold[0] 182 | template toRange*[T](a: Range[T]): Range[T] = a 183 | 184 | # this preferred syntax doesn't work 185 | # see https://github.com/nim-lang/Nim/issues/7995 186 | #template copyRange[T](dest: seq[T], destOffset: int, src: Range[T]) = 187 | # when supportsCopyMem(T): 188 | 189 | template copyRange[T](E: typedesc, dest: seq[T], destOffset: int, src: Range[T]) = 190 | when supportsCopyMem(E): 191 | if dest.len != 0 and src.len != 0: 192 | copyMem(dest[destOffset].unsafeAddr, src.start, sizeof(T) * src.len) 193 | else: 194 | for i in 0.. 0: 219 | if isNil(a.start) or a.mLen <= 0: 220 | res = false 221 | else: 222 | if a.mLen - b < 0: 223 | res = false 224 | else: 225 | a.start = a.start.shift(b) 226 | a.mLen -= b 227 | res = true 228 | res 229 | 230 | proc tryAdvance*[T](x: var Range[T], idx: int): bool = 231 | ## Move internal start offset of range ``x`` by ``idx`` elements forward. 232 | ## 233 | ## Returns ``true`` if operation got completed successfully, or 234 | ## ``false`` if you are trying to overrun range ``x``. 235 | result = x.advanceImpl(idx) 236 | 237 | proc tryAdvance*[T](x: var MutRange[T], idx: int): bool {.inline.} = 238 | ## Move internal start offset of range ``x`` by ``idx`` elements forward. 239 | ## 240 | ## Returns ``true`` if operation got completed successfully, or 241 | ## ``false`` if you are trying to overrun range ``x``. 242 | result = tryAdvance(Range[T](x), idx) 243 | 244 | proc advance*[T](x: var Range[T], idx: int) = 245 | ## Move internal start offset of range ``x`` by ``idx`` elements forward. 246 | let res = x.advanceImpl(idx) 247 | if not res: raise newException(IndexError, "Advance Error") 248 | 249 | proc advance*[T](x: var MutRange[T], idx: int) {.inline.} = 250 | ## Move internal start offset of range ``x`` by ``idx`` elements forward. 251 | advance(Range[T](x), idx) 252 | -------------------------------------------------------------------------------- /tests/all.nim: -------------------------------------------------------------------------------- 1 | import 2 | ttypedranges, tstackarrays, tbitranges 3 | -------------------------------------------------------------------------------- /tests/tbitranges.nim: -------------------------------------------------------------------------------- 1 | import 2 | random, unittest, 3 | ../ranges/bitranges 4 | 5 | proc randomBytes(n: int): seq[byte] = 6 | result = newSeq[byte](n) 7 | for i in 0 ..< result.len: 8 | result[i] = byte(rand(256)) 9 | 10 | suite "bit ranges": 11 | 12 | test "basic": 13 | var a = @[byte 0b10101010, 0b11110000, 0b00001111, 0b01010101] 14 | 15 | var bSeq = @[byte 0b10101010, 0b00000000, 0b00000000, 0b11111111] 16 | var b = bits(bSeq, 8) 17 | 18 | var cSeq = @[byte 0b11110000, 0b00001111, 0b00000000, 0b00000000] 19 | var c = bits(cSeq, 16) 20 | 21 | var dSeq = @[byte 0b00001111, 0b00000000, 0b00000000, 0b00000000] 22 | var d = bits(dSeq, 8) 23 | 24 | var eSeq = @[byte 0b01010101, 0b00000000, 0b00000000, 0b00000000] 25 | var e = bits(eSeq, 8) 26 | 27 | var m = a.bits 28 | var n = m[0..7] 29 | check n == b 30 | check n.len == 8 31 | check b.len == 8 32 | check c == m[8..23] 33 | check $(d) == "00001111" 34 | check $(e) == "01010101" 35 | 36 | var f = int.fromBits(e, 0, 4) 37 | check f == 0b0101 38 | 39 | let k = n & d 40 | check(k.len == n.len + d.len) 41 | check($k == $n & $d) 42 | 43 | var asciiSeq = @[byte('A'),byte('S'),byte('C'),byte('I'),byte('I')] 44 | let asciiBits = bits(asciiSeq) 45 | check $asciiBits == "0100000101010011010000110100100101001001" 46 | 47 | test "concat operator": 48 | randomize(5000) 49 | 50 | for i in 0..<256: 51 | var xSeq = randomBytes(rand(i)) 52 | var ySeq = randomBytes(rand(i)) 53 | let x = xSeq.bits 54 | let y = ySeq.bits 55 | var z = x & y 56 | check z.len == x.len + y.len 57 | check($z == $x & $y) 58 | 59 | test "get set bits": 60 | randomize(1000) 61 | 62 | for i in 0..<256: 63 | # produce random vector 64 | var xSeq = randomBytes(i) 65 | var ySeq = randomBytes(i) 66 | var x = xSeq.bits 67 | var y = ySeq.bits 68 | for idx, bit in x: 69 | y[idx] = bit 70 | check x == y 71 | 72 | test "constructor with start": 73 | var a = @[byte 0b10101010, 0b11110000, 0b00001111, 0b01010101] 74 | var b = a.bits(1, 8) 75 | check b.len == 8 76 | check b[0] == false 77 | check $b == "01010101" 78 | b[0] = true 79 | check $b == "11010101" 80 | check b[0] == true 81 | b.pushFront(false) 82 | check b[0] == false 83 | check $b == "011010101" 84 | -------------------------------------------------------------------------------- /tests/tstackarrays.nim: -------------------------------------------------------------------------------- 1 | import 2 | unittest, math, 3 | ../ranges/[stackarrays, ptr_arith] 4 | 5 | suite "Stack arrays": 6 | test "Basic operations work as expected": 7 | var arr = allocStackArray(int, 10) 8 | check: 9 | type(arr[0]) is int 10 | arr.len == 10 11 | 12 | # all items should be initially zero 13 | for i in arr: check i == 0 14 | for i in 0 .. arr.high: check arr[i] == 0 15 | 16 | arr[0] = 3 17 | arr[5] = 10 18 | arr[9] = 6 19 | 20 | check: 21 | sum(arr.toOpenArray) == 19 22 | arr[5] == 10 23 | arr[^1] == 6 24 | cast[ptr int](shift(addr arr[0], 5))[] == 10 25 | 26 | test "Allocating with a negative size throws a RangeError": 27 | expect RangeError: 28 | var arr = allocStackArray(string, -1) 29 | 30 | test "The array access is bounds-checked": 31 | var arr = allocStackArray(string, 3) 32 | arr[2] = "test" 33 | check arr[2] == "test" 34 | expect RangeError: 35 | arr[3] = "another test" 36 | 37 | test "proof of stack allocation": 38 | proc fun() = 39 | # NOTE: has to be inside a proc otherwise x1 not allocated on stack. 40 | var x1 = 0 41 | var arr = allocStackArray(int, 3) 42 | 43 | check: 44 | # stack can go either up or down, hence `abs`. 45 | # 1024 should be large enough (was 312 on OSX). 46 | abs(cast[int](x1.addr) - cast[int](addr(arr[0]))) < 1024 47 | fun() 48 | -------------------------------------------------------------------------------- /tests/ttypedranges.nim: -------------------------------------------------------------------------------- 1 | import 2 | unittest, sets, 3 | ../ranges/[typedranges, ptr_arith] 4 | 5 | suite "Typed ranges": 6 | test "basic stuff": 7 | var r = newRange[int](5) 8 | r[0] = 1 9 | r[1 .. ^1] = [2, 3, 4, 5] 10 | 11 | check $r == "R[1, 2, 3, 4, 5]" 12 | 13 | var s = newSeq[int]() 14 | for a in r: s.add(a) 15 | check s == @[1, 2, 3, 4, 5] 16 | 17 | test "subrange": 18 | var a = newRange[int](5) 19 | let b = toRange(@[1, 2, 3]) 20 | a[1 .. 3] = b 21 | check a.toSeq == @[0, 1, 2, 3, 0] 22 | check: 23 | a[2 .. 2].len == 1 24 | a[1 ..< 1].len == 0 25 | 26 | test "equality operator": 27 | var x = toRange(@[0, 1, 2, 3, 4, 5]) 28 | var y = x[1 .. ^2] 29 | var z = toRange(@[1, 2, 3, 4]) 30 | check y == z 31 | check x != z 32 | 33 | test "concat operation": 34 | var a = toRange(@[1,2,3]) 35 | var b = toRange(@[4,5,6]) 36 | var c = toRange(@[7,8,9]) 37 | var d = @[1,2,3,4,5,6,7,8,9] 38 | var e = @[1,2,3,4,5,6] 39 | var f = @[4,5,6,7,8,9] 40 | var x = concat(a, b, c) 41 | var y = a & b 42 | check x == d 43 | check y == e 44 | var z = concat(b, @[7,8,9]) 45 | check z == f 46 | 47 | let u = toRange(newSeq[int](0)) 48 | let v = toRange(@[3]) 49 | check concat(u, v) == @[3] 50 | check (v & u) == @[3] 51 | 52 | test "complex types concat operation": 53 | type 54 | Jaeger = object 55 | name: string 56 | weight: int 57 | 58 | var A = Jaeger(name: "Gipsy Avenger", weight: 2004) 59 | var B = Jaeger(name: "Striker Eureka", weight: 1850) 60 | var C = Jaeger(name: "Saber Athena", weight: 1628) 61 | var D = Jaeger(name: "Cherno Alpha", weight: 2412) 62 | 63 | var k = toRange(@[A, B]) 64 | var m = toRange(@[C, D]) 65 | var n = concat(k, m) 66 | check n == @[A, B, C ,D] 67 | check n != @[A, B, C ,C] 68 | 69 | test "shallowness": 70 | var s = @[1, 2, 3] 71 | var r = s.toRange() 72 | var r2 = r 73 | s[0] = 5 74 | check(r[0] == 5) 75 | s[1] = 10 76 | check(r2[1] == 10) 77 | var r3 = r[2..2] 78 | s[2] = 15 79 | check(r3[0] == 15) 80 | 81 | test "hash function": 82 | var a = toRange(@[1,2,3]) 83 | var b = toRange(@[4,5,6]) 84 | var c = toRange(@[7,8,9]) 85 | var d = toRange(@[1,2,3,4,5,6,7,8,9]) 86 | var e = toRange(@[1,2,3,4,5,6,7,8,9]) 87 | var x = toSet([a, b, c, a, b]) 88 | check x.len == 3 89 | check a in x 90 | 91 | var z = toRange(@[7,8,9]) 92 | var y = toSet([z, b, c]) 93 | check z in y 94 | check z in x 95 | 96 | var u = d[0..2] 97 | var v = d[3..5] 98 | var uu = e[0..2] 99 | var vv = e[3..5] 100 | check hash(u) != hash(v) 101 | check hash(uu) == hash(u) 102 | check hash(v) == hash(vv) 103 | check hash(uu) != hash(vv) 104 | 105 | test "toOpenArray": 106 | var a = toRange(@[1,2,3]) 107 | check $a.toOpenArray == "[1, 2, 3]" 108 | 109 | test "MutRange[T] shallow test": 110 | var b = @[1, 2, 3, 4, 5, 6] 111 | var r1 = b.toRange() 112 | var r2 = r1 113 | b[0] = 5 114 | b[1] = 10 115 | b[2] = 15 116 | var r3 = r1[1..1] 117 | var a0 = cast[uint](addr b[0]) 118 | var a1 = cast[uint](r1.gcHolder) 119 | var a2 = cast[uint](r2.gcHolder) 120 | var a3 = cast[uint](r3.gcHolder) 121 | check: 122 | a1 == a0 123 | a2 == a0 124 | a3 == a0 125 | 126 | test "Range[T] shallow test": 127 | var r1 = toRange(@[1, 2, 3, 4, 5, 6]) 128 | var r2 = r1 129 | var r3 = r1[1..1] 130 | var a1 = cast[uint](r1.gcHolder) 131 | var a2 = cast[uint](r2.gcHolder) 132 | var a3 = cast[uint](r3.gcHolder) 133 | check: 134 | a2 == a1 135 | a3 == a1 136 | 137 | test "tryAdvance(Range)": 138 | var a: Range[int] 139 | check: 140 | a.tryAdvance(1) == false 141 | a.tryAdvance(-1) == false 142 | a.tryAdvance(0) == true 143 | var b = toRange(@[1, 2, 3]) 144 | check: 145 | b.tryAdvance(-1) == false 146 | $b.toOpenArray == "[1, 2, 3]" 147 | b.tryAdvance(0) == true 148 | $b.toOpenArray == "[1, 2, 3]" 149 | b.tryAdvance(1) == true 150 | $b.toOpenArray == "[2, 3]" 151 | b.tryAdvance(1) == true 152 | $b.toOpenArray == "[3]" 153 | b.tryAdvance(1) == true 154 | $b.toOpenArray == "[]" 155 | b.tryAdvance(1) == false 156 | $b.toOpenArray == "[]" 157 | 158 | test "advance(Range)": 159 | template aecheck(a, b): int = 160 | var res = 0 161 | try: 162 | a.advance(b) 163 | res = 1 164 | except IndexError: 165 | res = 2 166 | res 167 | 168 | var a: Range[int] 169 | check: 170 | a.aecheck(1) == 2 171 | a.aecheck(-1) == 2 172 | a.aecheck(0) == 1 173 | var b = toRange(@[1, 2, 3]) 174 | check: 175 | b.aecheck(-1) == 2 176 | $b.toOpenArray == "[1, 2, 3]" 177 | b.aecheck(0) == 1 178 | $b.toOpenArray == "[1, 2, 3]" 179 | b.aecheck(1) == 1 180 | $b.toOpenArray == "[2, 3]" 181 | b.aecheck(1) == 1 182 | $b.toOpenArray == "[3]" 183 | b.aecheck(1) == 1 184 | $b.toOpenArray == "[]" 185 | b.aecheck(1) == 2 186 | $b.toOpenArray == "[]" 187 | 188 | test "tryAdvance(MutRange)": 189 | var a: MutRange[int] 190 | check: 191 | a.tryAdvance(1) == false 192 | a.tryAdvance(-1) == false 193 | a.tryAdvance(0) == true 194 | var buf = @[1, 2, 3] 195 | var b = toRange(buf) 196 | check: 197 | b.tryAdvance(-1) == false 198 | $b.toOpenArray == "[1, 2, 3]" 199 | b.tryAdvance(0) == true 200 | $b.toOpenArray == "[1, 2, 3]" 201 | b.tryAdvance(1) == true 202 | $b.toOpenArray == "[2, 3]" 203 | b.tryAdvance(1) == true 204 | $b.toOpenArray == "[3]" 205 | b.tryAdvance(1) == true 206 | $b.toOpenArray == "[]" 207 | b.tryAdvance(1) == false 208 | $b.toOpenArray == "[]" 209 | 210 | test "advance(MutRange)": 211 | template aecheck(a, b): int = 212 | var res = 0 213 | try: 214 | a.advance(b) 215 | res = 1 216 | except IndexError: 217 | res = 2 218 | res 219 | 220 | var a: MutRange[int] 221 | check: 222 | a.aecheck(1) == 2 223 | a.aecheck(-1) == 2 224 | a.aecheck(0) == 1 225 | var buf = @[1, 2, 3] 226 | var b = toRange(buf) 227 | check: 228 | b.aecheck(-1) == 2 229 | $b.toOpenArray == "[1, 2, 3]" 230 | b.aecheck(0) == 1 231 | $b.toOpenArray == "[1, 2, 3]" 232 | b.aecheck(1) == 1 233 | $b.toOpenArray == "[2, 3]" 234 | b.aecheck(1) == 1 235 | $b.toOpenArray == "[3]" 236 | b.aecheck(1) == 1 237 | $b.toOpenArray == "[]" 238 | b.aecheck(1) == 2 239 | $b.toOpenArray == "[]" 240 | 241 | test "make openarrays from pointers": 242 | var str = "test 1,2,3" 243 | var charPtr: ptr char = addr str[7] 244 | var regularPtr: pointer = addr str[5] 245 | 246 | check: 247 | # (regularPtr.makeOpenArray(char, 4).len == 4) 248 | (regularPtr.makeOpenArray(char, 5) == "1,2,3") 249 | (regularPtr.makeOpenArray(char, 5) == str[5..9]) 250 | 251 | # (charPtr.makeOpenArray(3).len == 3) 252 | (charPtr.makeOpenArray(3) == "2,3") 253 | (charPtr.makeOpenArray(1) == str[7..7]) 254 | 255 | --------------------------------------------------------------------------------