1 /**
2     DataNode Array Abstraction
3 
4     Copyright: 
5         Copyright © 2020-2026, Inochi2D Project
6     
7     License:
8         $(LINK2 https://github.com/Inochi2D/inochi2d/blob/main/LICENSE, BSD 2-clause License)
9     
10     Authors:
11         Luna Nielsen
12 */
13 module inp.format.array;
14 import nulib.collections.internal.marray;
15 import numem.core.traits;
16 import numem.core.memory;
17 import numem.rc;
18 import numem;
19 
20 /**
21     A refcounted array
22 */
23 struct RcArray(T) {
24 private:
25 @nogc:
26     alias VStoreT = ManagedArray!T;
27     Rc!VStoreT values;
28 
29 public:
30     alias data this;
31 
32     /**
33         Length of the array.
34     */
35     @property size_t length() pure => values ? values.length : 0;
36 
37     /**
38         The data stored in the refcounted array.
39     */
40     @property T[] data() => values ? values.value : null;
41 
42     /**
43         Makes a new array.
44     */
45     static typeof(this) make() {
46         typeof(this) result;
47         result.values = Rc!(VStoreT)(VStoreT());
48         return result;
49     }
50 
51     /// Destructor
52     ~this() nothrow @trusted {
53         if (values) {
54             values.release();
55         }
56     }
57 
58     /**
59         Creates a new RC array with values copied from the given slice.
60 
61         Params:
62             values = Slice of values to put into the array
63     */
64     this(T[] values) {
65         this.values = Rc!(VStoreT)(VStoreT());
66         this.values.resize(values.length);
67         nu_memcpy(&this.values, &values, values.length*T.sizeof);
68     }
69 
70     /**
71         Copy-constructor
72     */
73     this()(auto ref return scope inout(typeof(this)) rhs) pure nothrow @trusted {
74         nu_memmove(&values, &rhs.values, typeof(values).sizeof);
75         if (values)
76             values.retain();
77     }
78 
79     /**
80         Assigns an element of the array.
81 
82         Params:
83             i =     The index to set.
84             value = The value to set.
85     */
86     void opIndexAssign()(auto ref TValue value, size_t i) {
87         assert(values);
88         assert(i < length);
89 
90         this.values[i] = value;
91     }
92 
93     /**
94         Assignment operator
95     */
96     void opAssign()(auto ref return scope inout(typeof(this)) rhs) {
97         if (values)
98             values.release();
99         
100         nu_memmove(&values, &rhs.values, typeof(values).sizeof);
101         if (values)
102             values.retain();
103     }
104 
105     /**
106         Adds the given entry into the array.
107 
108         Params:
109             rhs = Value to append
110     */
111     void opOpAssign(string op)(T rhs)
112     if (op == "~") {
113         this.values.resize(values.length+1);
114         this.values[$-1] = rhs;
115     }
116 
117     /**
118         Adds the given entry into the array.
119 
120         Params:
121             rhs = Value to append
122     */
123     void opOpAssign(string op)(T[] rhs)
124     if (op == "~") {
125         assert(rhs.ptr-values.ptr >= 0);
126 
127         ptrdiff_t copyStart = rhs.ptr-values.ptr;
128         ptrdiff_t copyLength = rhs.length;
129         ptrdiff_t copyEnd = copyOffset+copyLength;
130         bool overlap = nu_is_overlapping(values.ptr, values.length, rhs.ptr, rhs.length);
131         this.values.resize(values.length+rhs.length);
132 
133         // On overlap we need to readjust our input slice.
134         if (overlap) 
135             rhs = values[copyStart..copyEnd];
136 
137         this.values[copyEnd..copyEnd+copyLength] = rhs[copyStart..copyEnd];
138     }
139 
140     /**
141         Removes the given key from the object.
142 
143         Params:
144             i = The index to remove.
145     */
146     void remove()(size_t i) {
147         assert(i < values.length);
148         values.deleteRange(values[i..i+1]);
149     }
150 
151     /**
152         Resizes the reference counted array.
153 
154         Params:
155             length = The new length.
156     */
157     void resize(size_t length) {
158         this.values.resize(length);
159     }
160 
161     /**
162         Indexes the array.
163 
164         Params:
165             i = The index.
166 
167         Returns:
168             The item at the index.
169     */
170     auto ref T opIndex()(size_t i) {
171         assert(i < values.value.length);
172         return values[i];
173     }
174 
175     /**
176         Dict-iterator
177     */
178     int opApply(scope int delegate(ref size_t index, ref T value) dg) {
179         if (!values)
180             return 0;
181         
182         auto dgf = cast(int delegate(ref size_t index, ref T value) @nogc scope)dg;
183         foreach (i; 0..values.length) {
184             int result = dgf(i, values[i]);
185             if (result)
186                 return result;
187         }
188         return 0;
189     }
190 
191     /**
192         Dict-iterator
193     */
194     int opApply(scope int delegate(ref T value) dg) {
195         if (!values)
196             return 0;
197         
198         auto dgf = cast(int delegate(ref T value) @nogc scope)dg;
199         foreach (i; 0..values.length) {
200             int result = dgf(values[i]);
201             if (result)
202                 return result;
203         }
204         return 0;
205     }
206 }
207 
208 /// UTF8 refcounted string
209 alias RcString = RcArray!char;