Jackalope / jrf (public) (License: GPLv3 or later version) (since 2019-11-21) (hash sha1)
Libriary for reading and writing resource files: vertices, indices, meshes, images, models and scene data.
Supports reading from filesystem as well as from zip files.
It uses unique binary container format for those resources.
It is used in tool for converting popular formats like wavefront .obj file, TARGA image to this container and also used in resource loader as part of graphics framework.

It requires jlib libriary (see my user repositories) and libzip.
It is possible to remove libzip dependency.
List of commits:
Subject Hash Author Date (UTC)
Wavefront obj file converter. aa9023cf916fb5af85bc181a04c3e9252f97c098 Jackalope 2020-06-03 01:45:56
Forget to add config to installation! 9eb91f41049b2f0dcda1442c1750da4266e97376 Jackalope 2020-06-01 16:26:51
Better config, solving some issues with linking to jrf. 11ab2d21090cf684c08954ffabea0fe317afcc0a Jackalope 2020-06-01 16:24:51
Link jrf-shared to jlib-shared and jrf-static to jrf-static. b965856e0f894aa48555b9ccfef812bd15d2e3a6 Jackalope 2020-06-01 16:23:26
Version 0.2.2 d6aaf170a2db27ddcd429a89fd873341ea8a0082 Jackalope 2020-05-31 23:47:23
Move definitions to configuration file. a7b28738de65520eddd08cbd5d8c958fa16eedbe Jackalope 2020-05-31 21:45:28
CMake: build both shared and static with JRF_SHARED and JRF_STATIC options. When is subdirectory, select only one instead. 0dc49150959af90cd9210367a61113eca85650eb Jackalope 2020-05-31 21:38:50
CMake: version check changed. 500bcbe11c7f1e418e5d8116e89654d160c8dbd6 Jackalope 2020-05-31 21:37:47
Reduced warnings. 865f2c52ed60c37c050a1033905bb2df278032a0 Jackalope 2020-05-27 15:03:07
Make "find_package(jlib 0.2.0 REQUIRED)" optional 58bfc04890fab24adf5fc84963cb1a3bca7a8706 Jackalope 2020-05-24 16:39:14
changed notice about native compilation 0ed7e9e5e72a2d13e8add619f7999a51634f8e9c Jackalope 2020-05-24 16:37:54
Version 0.2.1 02d30aa69e9026a3f96d8549a297fccfbefbda47 Jackalope 2020-05-24 14:31:31
Why there is Doxyfile? 16cf9fe9e64353a2a717668cc4083465e2f8dff5 Jackalope 2020-05-24 14:25:20
Add license notice to CMakeLists.txt db2e7f018714cd2643c7928a8cce966d3da25059 Jackalope 2020-05-24 14:23:39
Make zip functionality optional 8e4409ba503637e5981f98c4df39ff4d649351de Jackalope 2020-05-24 14:23:18
remove LTO option 9b39746de0abc22a2ae8d2ce8c2db253dd9ad71a Jackalope 2020-05-24 13:41:20
more lost changes c52ad1ddc9fe753cb2ec6f388e4a88f9dfb0fadf Jackalope 2020-05-24 13:30:51
added lost changes 1e60b3d0d8ecda7995f3a102257d8f3c0ca4b15e Jackalope 2020-05-24 13:27:42
CMake installation target 57ba69a5bc17124a96b401c20e68f1602345c4f7 Jackalope 2020-05-24 13:22:15
Moved read,write functions to read.h,write.h, merged vertices,indices and mesh files to mesh.h 365bcb01b01e95448e223de82e9f9e96b51fa789 Jackalope 2020-05-24 13:21:23
Commit aa9023cf916fb5af85bc181a04c3e9252f97c098 - Wavefront obj file converter.
Author: Jackalope
Author date (UTC): 2020-06-03 01:45
Committer name: Jackalope
Committer date (UTC): 2020-06-05 00:35
Parent(s): 9eb91f41049b2f0dcda1442c1750da4266e97376
Signing key:
Tree: b7ec4fc37e9fd3e4ad7923bb1b0c803fb039f6f5
File Lines added Lines deleted
CMakeLists.txt 4 0
include/jrf/number.h 83 0
include/jrf/wavefront_obj.h 112 0
src/wavefront_obj.cpp 359 0
File CMakeLists.txt changed (mode: 100644) (index 5f8009c..e781f5d)
... ... set(INCLUDE_FILES
46 46 read.h read.h
47 47 write.h write.h
48 48
49 number.h
50 wavefront_obj.h
51
49 52 config.h config.h
50 53 ) )
51 54 if (JRF_ZIP) if (JRF_ZIP)
 
... ... list(TRANSFORM INCLUDE_FILES PREPEND jrf/)
56 59 set(SOURCE_FILES set(SOURCE_FILES
57 60 src/result.cpp src/result.cpp
58 61 src/convert.cpp src/convert.cpp
62 src/wavefront_obj.cpp
59 63 ) )
60 64
61 65 configure_file( configure_file(
File include/jrf/number.h added (mode: 100644) (index 0000000..75d032e)
1 /**
2 * Copyright 2020 Damir Valiev
3 *
4 * This file is part of jrf C++ library.
5 *
6 * This library is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this library. If not, see <https://www.gnu.org/licenses/>
18 */
19 #pragma once
20 #include <memory>
21 #include <cinttypes>
22 #include <charconv>
23
24 namespace jrf {
25 struct Float;
26 }
27
28 struct jrf::Float {
29 [[nodiscard]] static Float
30 from_str(const char* p_str, uint64_t limit, uint64_t *p_readed_dst) {
31 bool is_negative = false;
32 uint64_t integer = 0;
33 uint64_t fraction = 0;
34 uint64_t fraction_scale = 1;
35
36 uint64_t &i = *p_readed_dst;
37 i = 0;
38 if (p_str[i] == '-') {
39 ++i;
40 is_negative = true;
41 }
42 else if (p_str[i] == '+')
43 ++i;
44
45 if (i >= limit) {
46 i = 0;
47 return Float{};
48 }
49
50 while (p_str[i] >= '0' && p_str[i] <= '9') {
51 integer *= 10;
52 integer += uint64_t(p_str[i] - '0');
53 ++i;
54 if (i >= limit)
55 goto BUILT;
56 }
57 if (p_str[i] != '.')
58 goto BUILT;
59 ++i;
60
61 while (p_str[i] >= '0' && p_str[i <= '9']) {
62 fraction *= 10;
63 fraction += uint64_t(p_str[i] - '0');
64 fraction_scale *= 10;
65 ++i;
66 if (i >= limit)
67 break;
68 }
69 BUILT:
70 Float result;
71 result._ = double(integer) + double(fraction) / double(fraction_scale);
72 result._ *= 1 - 2 * is_negative;
73 return result;
74 }
75 [[nodiscard]] double to_double() const { return double(_); }
76 [[nodiscard]] float to_float() const { return float(_); }
77
78 [[nodiscard]] bool operator != (const Float&__) const {
79 return memcmp(&_,&__, sizeof(_)) != 0;
80 }
81 private:
82 double _;
83 };
File include/jrf/wavefront_obj.h added (mode: 100644) (index 0000000..9d80b2c)
1 /**
2 * Copyright 2020 Damir Valiev
3 *
4 * This file is part of jrf C++ library.
5 *
6 * This library is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this library. If not, see <https://www.gnu.org/licenses/>
18 */
19 #pragma once
20 #include <jlib/darray.h>
21 #include <jlib/array.h>
22 #include <jlib/string.h>
23
24 namespace jrf::wavefront_obj {
25 enum Result {
26 SUCCESS,
27 ALLOCATION_FAIL,
28 EXPECTED_INDEX,
29 EXPECTED_VERTEX,
30 UNEXPECTED_SYMBOL,
31 INDEX_VALUE_OVERFLOW,
32 INDEX_CANT_BE_ZERO
33 };
34 const char*
35 to_str(Result result);
36
37 using Triangle = jl::array<uint32_t, 3>;
38 using Coordinates = jl::rarray<float>;
39 using Triangles = jl::rarray<Triangle>;
40 using Indices = jl::rarray<uint32_t>;
41
42 namespace AttributeN { enum T : uint32_t {
43 POSITION, TEXTURE_COORDINATES, NORMAL
44 }; }
45 using Attribute = AttributeN::T;
46 constexpr static const uint8_t ATTR_COUNT = Attribute::NORMAL+1;
47
48 using Defaults = jl::array<float, 4>;
49
50 template<typename T>
51 using PerAttr = jl::array<T, ATTR_COUNT>;
52
53 using CoordinatesPerAttr = PerAttr<Coordinates>;
54 using CoordinatesPtrsPerAttr = PerAttr<float*>;
55 using TrianglesPerAttr = PerAttr<Triangles>;
56 using DimensionsPerAttr = PerAttr<uint8_t>;
57 using DefaultsPerAttr = PerAttr<Defaults>;
58
59 struct ParseInfo {
60 jl::string_ro text;
61 DimensionsPerAttr dimensions;
62 DefaultsPerAttr defaults;
63 bool read_coordinates;
64 bool read_indices;
65 };
66
67 /// @brief Convert textual representation of wavefront obj to binary data.
68 /// Binary data is just binary representation of text data with triangles and
69 /// vertices, if there is several attributes with different indices -
70 /// data cannot be used directly for rendering, because there is
71 /// "(vertices+indices)-per-attr" instead of "(vertices-per-attr)+indices"
72 /// Data can be decoded to vertices array per attribute and then
73 /// compressed to vertices per attribute + indices.
74 /// @see decode
75 /// @see compress
76 /// Parser is don't care about many features of obj file, so it will return
77 /// an error only if vertex or face text is not followed by correct syntax.
78 /// Parser does not care about amount of vertices per attribute:
79 /// is it equal or not, it will succeed.
80 /// It is possible that there will be no data in textual representation
81 /// filtered with provided ParseInfo options,
82 /// SUCCESS will be returned anyway.
83 /// @return Almost any error. If not SUCCESS, nothing will be created and
84 /// output arguments will be cleared.
85 [[nodiscard]] Result
86 parse(const ParseInfo &in_info,
87 CoordinatesPerAttr *out_p_coordinates_per_attribute,
88 TrianglesPerAttr *out_p_triangle_indices_per_attribute,
89 uint64_t *out_p_char_error_ind);
90
91 /// @brief Convert vertices+indices representation to vertices only.
92 /// @return SUCCESS, ALLOCATION_FAIL, INDEX_VALUE_OVERFLOW
93 [[nodiscard]] Result
94 decode(const Coordinates &in_encoded_coordinates,
95 uint8_t in_dimension_count,
96 const Triangles &in_decoders_triangle_indices,
97 Coordinates *out_p_decoded_coordinates);
98
99 struct CompressInfo {
100 PerAttr<const float*> coordinates;
101 uint64_t count;
102 DimensionsPerAttr dimensions;
103 };
104 /// @brief Compress vertices-per-attr to (vertices-per-attr)+indices.
105 /// Very slow. Indices will represent triangles.
106 /// @return SUCCESS, ALLOCATION_FAIL, INDEX_VALUE_OVERFLOW
107 [[nodiscard]] Result
108 compress(const CompressInfo &info,
109 CoordinatesPtrsPerAttr *p_out_coordinates_per_attr,
110 uint64_t *p_out_vertex_count,
111 Indices *p_out_indices);
112 }
File src/wavefront_obj.cpp added (mode: 100644) (index 0000000..deaf778)
1 /**
2 * Copyright 2020 Damir Valiev
3 *
4 * This file is part of jrf C++ library.
5 *
6 * This library is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this library. If not, see <https://www.gnu.org/licenses/>
18 */
19 #include <jrf/wavefront_obj.h>
20 #include <jrf/number.h>
21 #include <limits>
22 using namespace jrf::wavefront_obj;
23
24 enum DataType : uint8_t {
25 POSITION, TEXTURE_COORDINATE, NORMAL, FACE, INVALID
26 };
27
28 const char *
29 jrf::wavefront_obj::to_str(Result result) {
30 #define STR(x) case Result::x: return #x;
31 switch (result) {
32 STR(SUCCESS)
33 STR(ALLOCATION_FAIL)
34 STR(EXPECTED_INDEX)
35 STR(EXPECTED_VERTEX)
36 STR(UNEXPECTED_SYMBOL)
37 STR(INDEX_VALUE_OVERFLOW)
38 STR(INDEX_CANT_BE_ZERO)
39 }
40 return "unknown error";
41 #undef STR
42 }
43
44 /// @param p_i Character index: in and out
45 [[nodiscard]] static bool
46 skip_junk(jl::string_ro code, uint64_t *p_i, bool skip_new_line = true) {
47 ONE_MORE_TIME:
48 if (*p_i >= code.count())
49 return false;
50 switch(code[*p_i]) {
51 case '\n': if (not skip_new_line)
52 return true;
53 [[fallthrough]];
54 case ' ': {
55 ++(*p_i);
56 goto ONE_MORE_TIME;
57 }
58 case '#': {
59 for(++(*p_i); *p_i < code.count(); ++(*p_i)) {
60 if (code[*p_i] == '\n') {
61 ++(*p_i);
62 goto ONE_MORE_TIME;
63 }
64 }
65 return false;
66 }
67 default: return true;
68 }
69 };
70
71 /// @param p_i Character index: in and out
72 /// Any junk is ignored, not an error
73 [[nodiscard]] static DataType
74 read_type(jl::string_ro code, uint64_t *p_i) {
75 char sym = code[*p_i];
76 ++(*p_i);
77 switch(sym) {
78 default:
79 case 'g':
80 case '\0':
81 case '\n':
82 return DataType::INVALID;
83 case 'v': {
84 if (code.count() > 1) {
85 sym = code[*p_i];
86 if (sym == 'n') {
87 ++(*p_i);
88 return DataType::NORMAL;
89 }
90 if (sym == 't') {
91 ++(*p_i);
92 return TEXTURE_COORDINATE;
93 }
94 }
95 return DataType::POSITION;
96 }
97 case 'f':
98 return DataType::FACE;
99 }
100 };
101
102 using CoordinateStream = jl::darray<float>;
103
104 /// @param p_i Character index: in and out
105 [[nodiscard]] static jrf::wavefront_obj::Result
106 read_vert(jl::string_ro code, uint8_t dims, const Defaults &defaults,
107 CoordinateStream *p_cs, uint64_t *p_i)
108 {
109 uint64_t &i = *p_i;
110 uint8_t j = 0;
111 for (; j < dims; ++j) {
112 float value;
113 uint64_t readed;
114 if (not skip_junk(code, &i, false)
115 or code[i] == '\n') {
116 UNREADED:
117 break;
118 }
119 value = jrf::Float::from_str(code.begin() + i, code.count() - i, &readed)
120 .to_float();
121 if (readed == 0)
122 goto UNREADED;
123 if (not p_cs->insert(value))
124 return Result::ALLOCATION_FAIL;
125 i += readed;
126 }
127 for (; j < dims; ++j) {
128 if (not p_cs->insert(defaults[j]))
129 return Result::ALLOCATION_FAIL;
130 }
131 for (; i < code.count(); ++i) {
132 if (code[i] == '\n') {
133 ++i;
134 break;
135 }
136 }
137 return Result::SUCCESS;
138 };
139
140 [[nodiscard]] static uint32_t
141 index_from_str(const char* p_str, uint64_t str_len, uint64_t *p_readed_dst) {
142 uint32_t integer = 0;
143 uint64_t &i = *p_readed_dst;
144 for (i = 0; i < str_len and p_str[i] >= '0' and p_str[i] <= '9'; ++i)
145 integer = integer * 10 + uint32_t(p_str[i] - '0');
146 return integer;
147 }
148
149 using TriangleStream = jl::darray<Triangle>;
150 using TriangleSrreamPerAttr = PerAttr<TriangleStream>;
151
152 [[nodiscard]] static jrf::wavefront_obj::Result
153 read_face(jl::string_ro code, const DimensionsPerAttr &attr_flags,
154 uint64_t *p_i, TriangleSrreamPerAttr *p_is) {
155 uint64_t &i = *p_i;
156 uint32_t ind_count = 0;
157 uint32_t ind_i = 0;
158
159 jl::array<Triangle, ATTR_COUNT> triangle_attrs;
160 bool is_complete_triangle = false;
161 NEXT_I:
162 if (not skip_junk(code, &i, false))
163 return Result::EXPECTED_INDEX;
164
165 if (code[i] == '\n')
166 return is_complete_triangle ? Result::SUCCESS : EXPECTED_INDEX;
167
168 if (is_complete_triangle) {
169 for (uint32_t attr_i = 0; attr_i < ATTR_COUNT; ++attr_i)
170 triangle_attrs[attr_i][1] = triangle_attrs[attr_i][0];
171 ind_i = 0;
172 }
173
174 for (uint32_t attr_i = 0; attr_i < ATTR_COUNT; ++attr_i) {
175 uint64_t readed;
176 uint32_t value;
177 value = index_from_str(code.begin() + i, code.count() - i, &readed);
178 i += readed;
179 if (readed == 0) {
180 if (attr_i == 0)
181 return Result::EXPECTED_INDEX;
182 triangle_attrs[attr_i][ind_i] = triangle_attrs[0][ind_i];
183 }
184 else {
185 if (value == 0)
186 return Result::INDEX_CANT_BE_ZERO;
187 triangle_attrs[attr_i][ind_i] = value - 1;
188 }
189 if (code[i] == '/') {
190 ++i;
191 continue;
192 }
193 }
194
195 ++ind_i;
196 ++ind_count;
197
198 is_complete_triangle = ind_count >= 3;
199 if (is_complete_triangle) {
200 for (uint32_t attr_i = 0; attr_i < ATTR_COUNT; ++attr_i) {
201 if (attr_flags[attr_i]
202 and
203 not (*p_is)[attr_i].insert(triangle_attrs[attr_i]))
204 return Result::ALLOCATION_FAIL;
205 }
206 }
207 goto NEXT_I;
208 };
209
210 [[nodiscard]] Result jrf::wavefront_obj::
211 parse(const ParseInfo &info,
212 CoordinatesPerAttr *p_coordinates_out,
213 TrianglesPerAttr *p_triangle_out,
214 uint64_t *p_char_error_ind)
215 {
216 *p_coordinates_out = {};
217 *p_triangle_out = {};
218 if (not (info.read_indices or info.read_indices))
219 return Result::SUCCESS;
220
221 PerAttr<CoordinateStream> coords = {};
222 TriangleSrreamPerAttr triangles = {};
223
224 Result res;
225 uint64_t &i = *p_char_error_ind;
226 auto &text = info.text;
227
228 for (i = 0; i < text.count();) {
229 if (not skip_junk(text, &i))
230 break;
231
232 DataType dtype = read_type(text, &i);
233 if (dtype < ATTR_COUNT and info.read_coordinates)
234 {
235 if (not info.dimensions[dtype])
236 goto SKIP;
237 res = read_vert(text, info.dimensions[dtype], info.defaults[dtype],
238 &coords[dtype], &i);
239 if (res != Result::SUCCESS)
240 goto CANCEL;
241 }
242 else if (dtype == FACE and info.read_indices)
243 {
244 res = read_face(text, info.dimensions, &i, &triangles);
245 if (res != Result::SUCCESS)
246 goto CANCEL;
247 }
248 else {
249 SKIP:
250 for(; i < text.count(); ++i) {
251 if (text[i] == '\n') {
252 ++i;
253 break;
254 }
255 }
256 }
257 }
258 for (uint32_t attr_i = 0; attr_i < ATTR_COUNT; ++attr_i) {
259 auto &cs = coords[attr_i];
260 (*p_coordinates_out)[attr_i] = {cs.begin(), cs.end()};
261 auto &ts = triangles[attr_i];
262 (*p_triangle_out)[attr_i] = {ts.begin(), ts.end()};
263 }
264 return Result::SUCCESS;
265
266 CANCEL:
267 coords.destroy();
268 triangles.destroy();
269 return res;
270 }
271
272 [[nodiscard]] Result jrf::wavefront_obj::
273 decode(const Coordinates &in_coordinates,
274 uint8_t dimension_count,
275 const Triangles &in_triangle_indices,
276 Coordinates *p_out_coordinates)
277 {
278 uint64_t decoded_count = in_triangle_indices.count() * 3 * dimension_count;
279 if (not p_out_coordinates->init(decoded_count))
280 return Result::ALLOCATION_FAIL;
281 float *p_dst = p_out_coordinates->begin();
282 for (auto &triangle : in_triangle_indices) {
283 for (auto &index : triangle) {
284 const float *p_src = in_coordinates.begin() + index * dimension_count;
285 if (p_src >= in_coordinates.end())
286 return Result::INDEX_VALUE_OVERFLOW;
287 memcpy(p_dst, p_src, sizeof(float) * dimension_count);
288 p_dst += dimension_count;
289 }
290 }
291 return Result::SUCCESS;
292 }
293
294 [[nodiscard]] Result jrf::wavefront_obj::
295 compress(const CompressInfo &info,
296 CoordinatesPtrsPerAttr *p_out_coordinates_per_attr,
297 uint64_t *p_out_vertex_count,
298 Indices *p_out_indices)
299 {
300 *p_out_coordinates_per_attr = {};
301 *p_out_indices = {};
302
303 if (info.count > std::numeric_limits<uint32_t>::max())
304 return Result::INDEX_VALUE_OVERFLOW;
305
306 auto &out_csas = *p_out_coordinates_per_attr;
307 auto &out_is = *p_out_indices;
308 uint64_t vcount = 0;
309
310 for (uint32_t attr_i = 0; attr_i < ATTR_COUNT; ++attr_i) {
311 auto &out_cs = out_csas[attr_i];
312 if (info.coordinates[attr_i] == nullptr)
313 out_cs = nullptr;
314 else if (not jl::allocate(&out_cs, info.count * info.dimensions[attr_i]))
315 goto CANCEL;
316 }
317 if (not out_is.init(info.count))
318 goto CANCEL;
319
320 for (uint64_t i = 0; i < info.count; ++i) {
321 // Search if vertex is duplicate
322 uint64_t index;
323 for (index = 0; index < vcount; ++index) {
324 for (uint32_t attr_i = 0; attr_i < ATTR_COUNT; ++attr_i) {
325 if (info.coordinates[attr_i] == nullptr)
326 continue;
327 auto dc = info.dimensions[attr_i];
328 const float *p1 = info.coordinates[attr_i] + dc * i;
329 float *p2 = out_csas[attr_i] + dc * index;
330 jassert(p1 + dc <= info.coordinates[attr_i] + dc * info.count, "know you");
331 jassert(p2 + dc <= out_csas[attr_i] + info.count * dc, "you know");
332 if (memcmp(p1, p2, dc * sizeof(float)) != 0)
333 goto NEXT_SEARCH_INDEX;
334 }
335 goto FOUND;
336 NEXT_SEARCH_INDEX:;
337 }
338
339 for (uint32_t attr_i = 0; attr_i < ATTR_COUNT; ++attr_i) {
340 if (info.coordinates[attr_i] == nullptr)
341 continue;
342 auto dc = info.dimensions[attr_i];
343 const float *p_src = info.coordinates[attr_i] + dc * i;
344 float *p_dst = out_csas[attr_i] + dc * index;
345 memcpy(p_dst, p_src, dc * sizeof(float));
346 }
347 ++vcount;
348 FOUND:
349 out_is[i] = uint32_t(index);
350 }
351 *p_out_vertex_count = vcount;
352 return Result::SUCCESS;
353 CANCEL:
354 for (auto &p : out_csas)
355 if (p != nullptr)
356 jl::deallocate(&p);
357 p_out_indices->destroy();
358 return Result::ALLOCATION_FAIL;
359 }
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/Jackalope/jrf

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/Jackalope/jrf

Clone this repository using git:
git clone git://git.rocketgit.com/user/Jackalope/jrf

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main