| File: | nnc/ccv_nnc_tensor_io.c |
| Warning: | line 482, column 22 Although the value stored to 'tensor' is used in the enclosing expression, the value is never actually read from 'tensor' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | #include "ccv_nnc.h" |
| 2 | #include "ccv_nnc_easy.h" |
| 3 | #include "ccv_nnc_internal.h" |
| 4 | #include "ccv_internal.h" |
| 5 | #include "_ccv_nnc_symbolic_graph.h" |
| 6 | #include "3rdparty/sqlite3/sqlite3.h" |
| 7 | #include <limits.h> |
| 8 | #include <stdint.h> |
| 9 | #ifdef HAVE_CUDA1 |
| 10 | #include "gpu/ccv_nnc_compat.h" |
| 11 | #elif HAVE_MPS |
| 12 | #include "mps/ccv_nnc_mps.h" |
| 13 | #endif |
| 14 | |
| 15 | #ifdef NDEBUG |
| 16 | #define SQLITE_ENFORCE(stmt)((void) sizeof ((stmt) ? 1 : 0), __extension__ ({ if (stmt) ; else __assert_fail ("stmt", "ccv_nnc_tensor_io.c", 16, __extension__ __PRETTY_FUNCTION__); })) (void)(stmt) |
| 17 | #else |
| 18 | #define SQLITE_ENFORCEassert assert |
| 19 | #endif |
| 20 | |
| 21 | // SQLite INTEGER is 64-bit. Tensor IO already packs side metadata into the |
| 22 | // high 32 bits of type / datatype. Tensor formats are low 32-bit int enum |
| 23 | // values (NCHW/NHWC/CHWN), so bit 32 can mark that tensors.data is the head |
| 24 | // chunk. Always mask this out before assigning to ccv_nnc_tensor_param_t.format. |
| 25 | #define CCV_NNC_TENSOR_IO_SPLIT_FORMAT(((sqlite_int64)1) << 32) (((sqlite_int64)1) << 32) |
| 26 | #define CCV_NNC_TENSOR_IO_FORMAT_MASK0xffffffffll 0xffffffffll |
| 27 | #define CCV_NNC_TENSOR_IO_SPLIT_HEADROOM1024 1024 |
| 28 | |
| 29 | static int _ccv_nnc_tensor_io_blob_chunk_size(sqlite3* const conn) |
| 30 | { |
| 31 | const int limit = sqlite3_limit(conn, SQLITE_LIMIT_LENGTH0, -1); |
| 32 | if (limit <= 0) |
| 33 | return INT_MAX2147483647; |
| 34 | if (limit > CCV_NNC_TENSOR_IO_SPLIT_HEADROOM1024 * 2) |
| 35 | return limit - CCV_NNC_TENSOR_IO_SPLIT_HEADROOM1024; |
| 36 | return limit > 1 ? limit / 2 : 1; |
| 37 | } |
| 38 | |
| 39 | static int _ccv_nnc_tensor_io_delete_splits(sqlite3* const conn, const char* const name) |
| 40 | { |
| 41 | const char tensor_split_delete_qs[] = "DELETE FROM tensor_splits WHERE name=$name"; |
| 42 | sqlite3_stmt* tensor_split_delete_stmt = 0; |
| 43 | int rc = sqlite3_prepare_v2(conn, tensor_split_delete_qs, sizeof(tensor_split_delete_qs), &tensor_split_delete_stmt, 0); |
| 44 | if (rc != SQLITE_OK0) |
| 45 | return rc; |
| 46 | sqlite3_bind_text(tensor_split_delete_stmt, 1, name, -1, SQLITE_STATIC((sqlite3_destructor_type)0)); |
| 47 | rc = sqlite3_step(tensor_split_delete_stmt); |
| 48 | sqlite3_finalize(tensor_split_delete_stmt); |
| 49 | return rc == SQLITE_DONE101 ? SQLITE_OK0 : rc; |
| 50 | } |
| 51 | |
| 52 | static int _ccv_nnc_tensor_io_write_splits(sqlite3* const conn, const char* const name, const unsigned char* const data, const size_t data_size, const size_t offset, const int chunk_size) |
| 53 | { |
| 54 | const char tensor_split_insert_qs[] = |
| 55 | "REPLACE INTO tensor_splits " |
| 56 | "(name, part, data) VALUES ($name, $part, $data)"; |
| 57 | sqlite3_stmt* tensor_split_insert_stmt = 0; |
| 58 | int rc = sqlite3_prepare_v2(conn, tensor_split_insert_qs, sizeof(tensor_split_insert_qs), &tensor_split_insert_stmt, 0); |
| 59 | if (rc != SQLITE_OK0) |
| 60 | return rc; |
| 61 | size_t pos = offset; |
| 62 | int part = 0; |
| 63 | while (pos < data_size) |
| 64 | { |
| 65 | const size_t tail_size = data_size - pos; |
| 66 | const int write_size = (int)ccv_min(tail_size, (size_t)chunk_size)({ typeof (tail_size) _a = (tail_size); typeof ((size_t)chunk_size ) _b = ((size_t)chunk_size); (_a < _b) ? _a : _b; }); |
| 67 | sqlite3_bind_text(tensor_split_insert_stmt, 1, name, -1, SQLITE_STATIC((sqlite3_destructor_type)0)); |
| 68 | sqlite3_bind_int(tensor_split_insert_stmt, 2, part++); |
| 69 | rc = sqlite3_bind_blob(tensor_split_insert_stmt, 3, data + pos, write_size, SQLITE_STATIC((sqlite3_destructor_type)0)); |
| 70 | if (rc != SQLITE_OK0) |
| 71 | break; |
| 72 | rc = sqlite3_step(tensor_split_insert_stmt); |
| 73 | if (rc != SQLITE_DONE101) |
| 74 | break; |
| 75 | sqlite3_reset(tensor_split_insert_stmt); |
| 76 | sqlite3_clear_bindings(tensor_split_insert_stmt); |
| 77 | pos += write_size; |
| 78 | } |
| 79 | sqlite3_finalize(tensor_split_insert_stmt); |
| 80 | return rc == SQLITE_DONE101 ? SQLITE_OK0 : rc; |
| 81 | } |
| 82 | |
| 83 | static int _ccv_nnc_tensor_io_read_split_data(sqlite3* const conn, const char* const name, const void* const first_data, const size_t first_size, const void** const data_out, size_t* const data_size_out, unsigned char** const workspace_out) |
| 84 | { |
| 85 | const char tensor_split_size_qs[] = "SELECT COUNT(*), SUM(length(data)) FROM tensor_splits WHERE name=$name"; |
| 86 | sqlite3_stmt* tensor_split_size_stmt = 0; |
| 87 | int rc = sqlite3_prepare_v2(conn, tensor_split_size_qs, sizeof(tensor_split_size_qs), &tensor_split_size_stmt, 0); |
| 88 | if (rc != SQLITE_OK0) |
| 89 | return rc; |
| 90 | sqlite3_bind_text(tensor_split_size_stmt, 1, name, -1, SQLITE_STATIC((sqlite3_destructor_type)0)); |
| 91 | rc = sqlite3_step(tensor_split_size_stmt); |
| 92 | if (rc != SQLITE_ROW100) |
| 93 | { |
| 94 | sqlite3_finalize(tensor_split_size_stmt); |
| 95 | return rc; |
| 96 | } |
| 97 | const sqlite_int64 split_count = sqlite3_column_int64(tensor_split_size_stmt, 0); |
| 98 | const sqlite_int64 tail_size = sqlite3_column_int64(tensor_split_size_stmt, 1); |
| 99 | sqlite3_finalize(tensor_split_size_stmt); |
| 100 | if (split_count <= 0 || tail_size < 0 || (size_t)tail_size > SIZE_MAX(18446744073709551615UL) - first_size) |
| 101 | return SQLITE_CORRUPT11; |
| 102 | const size_t total_size = first_size + (size_t)tail_size; |
| 103 | unsigned char* const workspace = (unsigned char*)ccmallocmalloc(total_size); |
| 104 | if (first_size > 0) |
| 105 | memcpy(workspace, first_data, first_size); |
| 106 | const char tensor_split_select_qs[] = "SELECT part, data FROM tensor_splits WHERE name=$name ORDER BY part"; |
| 107 | sqlite3_stmt* tensor_split_select_stmt = 0; |
| 108 | rc = sqlite3_prepare_v2(conn, tensor_split_select_qs, sizeof(tensor_split_select_qs), &tensor_split_select_stmt, 0); |
| 109 | if (rc != SQLITE_OK0) |
| 110 | { |
| 111 | ccfreefree(workspace); |
| 112 | return rc; |
| 113 | } |
| 114 | sqlite3_bind_text(tensor_split_select_stmt, 1, name, -1, SQLITE_STATIC((sqlite3_destructor_type)0)); |
| 115 | size_t offset = first_size; |
| 116 | int expected_part = 0; |
| 117 | while ((rc = sqlite3_step(tensor_split_select_stmt)) == SQLITE_ROW100) |
| 118 | { |
| 119 | if (sqlite3_column_int(tensor_split_select_stmt, 0) != expected_part++) |
| 120 | { |
| 121 | sqlite3_finalize(tensor_split_select_stmt); |
| 122 | ccfreefree(workspace); |
| 123 | return SQLITE_CORRUPT11; |
| 124 | } |
| 125 | const int split_size = sqlite3_column_bytes(tensor_split_select_stmt, 1); |
| 126 | if (split_size < 0 || (size_t)split_size > total_size - offset) |
| 127 | { |
| 128 | sqlite3_finalize(tensor_split_select_stmt); |
| 129 | ccfreefree(workspace); |
| 130 | return SQLITE_CORRUPT11; |
| 131 | } |
| 132 | const void* const split_data = sqlite3_column_blob(tensor_split_select_stmt, 1); |
| 133 | if (split_size > 0) |
| 134 | memcpy(workspace + offset, split_data, split_size); |
| 135 | offset += split_size; |
| 136 | } |
| 137 | sqlite3_finalize(tensor_split_select_stmt); |
| 138 | if (rc != SQLITE_DONE101 || offset != total_size) |
| 139 | { |
| 140 | ccfreefree(workspace); |
| 141 | return rc == SQLITE_DONE101 ? SQLITE_CORRUPT11 : rc; |
| 142 | } |
| 143 | *data_out = workspace; |
| 144 | *data_size_out = total_size; |
| 145 | *workspace_out = workspace; |
| 146 | return SQLITE_OK0; |
| 147 | } |
| 148 | |
| 149 | // MARK - Level-1 API |
| 150 | |
| 151 | int ccv_nnc_tensor_write(const ccv_nnc_tensor_t* const tensor, void* const handle, const char* const name, const ccv_nnc_tensor_io_option_t* const options) |
| 152 | { |
| 153 | assert(CCV_IS_TENSOR_CONTIGUOUS(tensor))((void) sizeof (((!((*(int*)(tensor)) & CCV_TENSOR_VIEW) || (((ccv_nnc_tensor_view_t*)tensor)->contiguous == 1))) ? 1 : 0), __extension__ ({ if ((!((*(int*)(tensor)) & CCV_TENSOR_VIEW ) || (((ccv_nnc_tensor_view_t*)tensor)->contiguous == 1))) ; else __assert_fail ("CCV_IS_TENSOR_CONTIGUOUS(tensor)", "ccv_nnc_tensor_io.c" , 153, __extension__ __PRETTY_FUNCTION__); })); |
| 154 | assert(name)((void) sizeof ((name) ? 1 : 0), __extension__ ({ if (name) ; else __assert_fail ("name", "ccv_nnc_tensor_io.c", 154, __extension__ __PRETTY_FUNCTION__); })); |
| 155 | sqlite3* conn = (sqlite3*)handle; |
| 156 | if (!conn) |
| 157 | return CCV_IO_ERROR; |
| 158 | const char tensor_create_table_qs[] = "CREATE TABLE IF NOT EXISTS tensors " |
| 159 | "(name TEXT, type INTEGER, format INTEGER, datatype INTEGER, " |
| 160 | "dim BLOB, data BLOB, PRIMARY KEY (name))"; |
| 161 | SQLITE_ENFORCE(SQLITE_OK == sqlite3_exec(conn, tensor_create_table_qs, 0, 0, 0))((void) sizeof ((0 == sqlite3_exec(conn, tensor_create_table_qs , 0, 0, 0)) ? 1 : 0), __extension__ ({ if (0 == sqlite3_exec( conn, tensor_create_table_qs, 0, 0, 0)) ; else __assert_fail ( "SQLITE_OK == sqlite3_exec(conn, tensor_create_table_qs, 0, 0, 0)" , "ccv_nnc_tensor_io.c", 161, __extension__ __PRETTY_FUNCTION__ ); })); |
| 162 | const char tensor_insert_qs[] = |
| 163 | "REPLACE INTO tensors " |
| 164 | "(name, type, format, datatype, dim, data) VALUES (" |
| 165 | "$name, $type, $format, $datatype, $dim, $data)"; |
| 166 | sqlite3_stmt* tensor_insert_stmt = 0; |
| 167 | SQLITE_ENFORCE(SQLITE_OK == sqlite3_prepare_v2(conn, tensor_insert_qs, sizeof(tensor_insert_qs), &tensor_insert_stmt, 0))((void) sizeof ((0 == sqlite3_prepare_v2(conn, tensor_insert_qs , sizeof(tensor_insert_qs), &tensor_insert_stmt, 0)) ? 1 : 0), __extension__ ({ if (0 == sqlite3_prepare_v2(conn, tensor_insert_qs , sizeof(tensor_insert_qs), &tensor_insert_stmt, 0)) ; else __assert_fail ("SQLITE_OK == sqlite3_prepare_v2(conn, tensor_insert_qs, sizeof(tensor_insert_qs), &tensor_insert_stmt, 0)" , "ccv_nnc_tensor_io.c", 167, __extension__ __PRETTY_FUNCTION__ ); })); |
| 168 | sqlite3_bind_text(tensor_insert_stmt, 1, name, -1, 0); |
| 169 | ccv_nnc_tensor_param_t params = tensor->info; |
| 170 | const size_t data_size = ccv_nnc_tensor_data_size_without_padding(tensor->info); |
| 171 | unsigned char* workspace = 0; |
| 172 | const void* payload = tensor->data.u8; |
| 173 | size_t payload_size = data_size; |
| 174 | unsigned int identifier = 0; |
| 175 | #ifdef HAVE_CUDA1 |
| 176 | if (CCV_TENSOR_GET_MEMORY(tensor->info.type)((tensor->info.type) & 0x3) == CCV_TENSOR_GPU_MEMORY) |
| 177 | { |
| 178 | if (!options || !options->encode) |
| 179 | { |
| 180 | workspace = ccmallocmalloc(data_size); |
| 181 | cumemcpy(workspace, CCV_TENSOR_CPU_MEMORY, tensor->data.u8, tensor->info.type, data_size); |
| 182 | payload = workspace; |
| 183 | } else { |
| 184 | workspace = ccmallocmalloc(data_size * 2 + 4); |
| 185 | cumemcpy(workspace, CCV_TENSOR_CPU_MEMORY, tensor->data.u8, tensor->info.type, data_size); |
| 186 | size_t encoded_size = data_size + 4; |
| 187 | if (options->encode(workspace, data_size, tensor->info.datatype, tensor->info.dim, ccv_nnc_tensor_nd(tensor->info.dim), options->context, workspace + data_size, &encoded_size, ¶ms, &identifier)) |
| 188 | { |
| 189 | payload = workspace + data_size; |
| 190 | payload_size = encoded_size; |
| 191 | } |
| 192 | else |
| 193 | payload = workspace; |
| 194 | } |
| 195 | } else { |
| 196 | if (!options || !options->encode) |
| 197 | payload = tensor->data.u8; |
| 198 | else { |
| 199 | workspace = ccmallocmalloc(data_size + 4); |
| 200 | size_t encoded_size = data_size + 4; |
| 201 | if (options->encode(tensor->data.u8, data_size, tensor->info.datatype, tensor->info.dim, ccv_nnc_tensor_nd(tensor->info.dim), options->context, workspace, &encoded_size, ¶ms, &identifier)) |
| 202 | { |
| 203 | payload = workspace; |
| 204 | payload_size = encoded_size; |
| 205 | } |
| 206 | else |
| 207 | payload = tensor->data.u8; |
| 208 | } |
| 209 | } |
| 210 | #elif defined(HAVE_MPS) |
| 211 | if (CCV_TENSOR_GET_MEMORY(tensor->info.type)((tensor->info.type) & 0x3) == CCV_TENSOR_GPU_MEMORY) |
| 212 | { |
| 213 | if (!options || !options->encode) |
| 214 | { |
| 215 | workspace = ccmallocmalloc(data_size); |
| 216 | mpmemcpy(workspace, 0, CCV_TENSOR_CPU_MEMORY, tensor->data.u8, tensor->dataof, tensor->info.type, data_size); |
| 217 | payload = workspace; |
| 218 | } else { |
| 219 | workspace = ccmallocmalloc(data_size * 2 + 4); |
| 220 | mpmemcpy(workspace, 0, CCV_TENSOR_CPU_MEMORY, tensor->data.u8, tensor->dataof, tensor->info.type, data_size); |
| 221 | size_t encoded_size = data_size + 4; |
| 222 | if (options->encode(workspace, data_size, tensor->info.datatype, tensor->info.dim, ccv_nnc_tensor_nd(tensor->info.dim), options->context, workspace + data_size, &encoded_size, ¶ms, &identifier)) |
| 223 | { |
| 224 | payload = workspace + data_size; |
| 225 | payload_size = encoded_size; |
| 226 | } |
| 227 | else |
| 228 | payload = workspace; |
| 229 | } |
| 230 | } else { |
| 231 | if (!options || !options->encode) |
| 232 | payload = tensor->data.u8; |
| 233 | else { |
| 234 | workspace = ccmallocmalloc(data_size + 4); // Allocate extra 4 bytes in case we need to copy the QX tensor out. |
| 235 | size_t encoded_size = data_size + 4; |
| 236 | if (options->encode(tensor->data.u8, data_size, tensor->info.datatype, tensor->info.dim, ccv_nnc_tensor_nd(tensor->info.dim), options->context, workspace, &encoded_size, ¶ms, &identifier)) |
| 237 | { |
| 238 | payload = workspace; |
| 239 | payload_size = encoded_size; |
| 240 | } |
| 241 | else |
| 242 | payload = tensor->data.u8; |
| 243 | } |
| 244 | } |
| 245 | #else |
| 246 | if (!options || !options->encode) |
| 247 | payload = tensor->data.u8; |
| 248 | else { |
| 249 | workspace = ccmallocmalloc(data_size + 4); |
| 250 | size_t encoded_size = data_size + 4; |
| 251 | if (options->encode(tensor->data.u8, data_size, tensor->info.datatype, tensor->info.dim, ccv_nnc_tensor_nd(tensor->info.dim), options->context, workspace, &encoded_size, ¶ms, &identifier)) |
| 252 | { |
| 253 | payload = workspace; |
| 254 | payload_size = encoded_size; |
| 255 | } |
| 256 | else |
| 257 | payload = tensor->data.u8; |
| 258 | } |
| 259 | #endif |
| 260 | int result = SQLITE_TOOBIG18; |
| 261 | int bind_result = payload_size <= INT_MAX2147483647 ? sqlite3_bind_blob(tensor_insert_stmt, 6, payload, (int)payload_size, 0) : SQLITE_TOOBIG18; |
| 262 | if (bind_result == SQLITE_OK0) |
| 263 | { |
| 264 | sqlite3_bind_int64(tensor_insert_stmt, 2, ((sqlite_int64)identifier << 32) | params.type); |
| 265 | sqlite3_bind_int(tensor_insert_stmt, 3, params.format); |
| 266 | sqlite3_bind_int64(tensor_insert_stmt, 4, ((sqlite_int64)params.reserved << 32) | params.datatype); |
| 267 | sqlite3_bind_blob(tensor_insert_stmt, 5, params.dim, sizeof(params.dim), 0); |
| 268 | result = sqlite3_step(tensor_insert_stmt); |
| 269 | if (result == SQLITE_DONE101) |
| 270 | { |
| 271 | sqlite3_reset(tensor_insert_stmt); |
| 272 | sqlite3_clear_bindings(tensor_insert_stmt); |
| 273 | sqlite3_finalize(tensor_insert_stmt); |
| 274 | if (workspace) |
| 275 | free(workspace); |
| 276 | return CCV_IO_FINAL; |
| 277 | } |
| 278 | sqlite3_reset(tensor_insert_stmt); |
| 279 | sqlite3_clear_bindings(tensor_insert_stmt); |
| 280 | if (result != SQLITE_TOOBIG18) |
| 281 | { |
| 282 | sqlite3_finalize(tensor_insert_stmt); |
| 283 | if (workspace) |
| 284 | free(workspace); |
| 285 | return CCV_IO_ERROR; |
| 286 | } |
| 287 | } else { |
| 288 | sqlite3_reset(tensor_insert_stmt); |
| 289 | sqlite3_clear_bindings(tensor_insert_stmt); |
| 290 | if (bind_result != SQLITE_TOOBIG18) |
| 291 | { |
| 292 | sqlite3_finalize(tensor_insert_stmt); |
| 293 | if (workspace) |
| 294 | free(workspace); |
| 295 | return CCV_IO_ERROR; |
| 296 | } |
| 297 | } |
| 298 | if (sqlite3_exec(conn, "SAVEPOINT ccv_nnc_tensor_write", 0, 0, 0) != SQLITE_OK0) |
| 299 | { |
| 300 | sqlite3_finalize(tensor_insert_stmt); |
| 301 | if (workspace) |
| 302 | free(workspace); |
| 303 | return CCV_IO_ERROR; |
| 304 | } |
| 305 | const char tensor_split_create_table_qs[] = "CREATE TABLE IF NOT EXISTS tensor_splits " |
| 306 | "(name TEXT, part INTEGER, data BLOB, PRIMARY KEY (name, part))"; |
| 307 | if (sqlite3_exec(conn, tensor_split_create_table_qs, 0, 0, 0) != SQLITE_OK0) |
| 308 | { |
| 309 | sqlite3_finalize(tensor_insert_stmt); |
| 310 | sqlite3_exec(conn, "ROLLBACK TO ccv_nnc_tensor_write", 0, 0, 0); |
| 311 | sqlite3_exec(conn, "RELEASE ccv_nnc_tensor_write", 0, 0, 0); |
| 312 | if (workspace) |
| 313 | free(workspace); |
| 314 | return CCV_IO_ERROR; |
| 315 | } |
| 316 | sqlite3_bind_text(tensor_insert_stmt, 1, name, -1, 0); |
| 317 | const int chunk_size = _ccv_nnc_tensor_io_blob_chunk_size(conn); |
| 318 | assert(chunk_size > 0)((void) sizeof ((chunk_size > 0) ? 1 : 0), __extension__ ( { if (chunk_size > 0) ; else __assert_fail ("chunk_size > 0" , "ccv_nnc_tensor_io.c", 318, __extension__ __PRETTY_FUNCTION__ ); })); |
| 319 | const size_t first_size = ccv_min(payload_size, (size_t)chunk_size)({ typeof (payload_size) _a = (payload_size); typeof ((size_t )chunk_size) _b = ((size_t)chunk_size); (_a < _b) ? _a : _b ; }); |
| 320 | bind_result = first_size < payload_size ? sqlite3_bind_blob(tensor_insert_stmt, 6, payload, (int)first_size, SQLITE_STATIC((sqlite3_destructor_type)0)) : SQLITE_TOOBIG18; |
| 321 | if (bind_result == SQLITE_OK0) |
| 322 | bind_result = _ccv_nnc_tensor_io_delete_splits(conn, name); |
| 323 | if (bind_result == SQLITE_OK0) |
| 324 | bind_result = _ccv_nnc_tensor_io_write_splits(conn, name, (const unsigned char*)payload, payload_size, first_size, chunk_size); |
| 325 | if (bind_result != SQLITE_OK0) |
| 326 | { |
| 327 | sqlite3_finalize(tensor_insert_stmt); |
| 328 | sqlite3_exec(conn, "ROLLBACK TO ccv_nnc_tensor_write", 0, 0, 0); |
| 329 | sqlite3_exec(conn, "RELEASE ccv_nnc_tensor_write", 0, 0, 0); |
| 330 | if (workspace) |
| 331 | free(workspace); |
| 332 | return CCV_IO_ERROR; |
| 333 | } |
| 334 | sqlite3_bind_int64(tensor_insert_stmt, 2, ((sqlite_int64)identifier << 32) | params.type); |
| 335 | sqlite3_bind_int64(tensor_insert_stmt, 3, ((sqlite_int64)params.format & CCV_NNC_TENSOR_IO_FORMAT_MASK0xffffffffll) | CCV_NNC_TENSOR_IO_SPLIT_FORMAT(((sqlite_int64)1) << 32)); |
| 336 | sqlite3_bind_int64(tensor_insert_stmt, 4, ((sqlite_int64)params.reserved << 32) | params.datatype); |
| 337 | sqlite3_bind_blob(tensor_insert_stmt, 5, params.dim, sizeof(params.dim), 0); |
| 338 | result = sqlite3_step(tensor_insert_stmt); |
| 339 | sqlite3_reset(tensor_insert_stmt); |
| 340 | sqlite3_clear_bindings(tensor_insert_stmt); |
| 341 | sqlite3_finalize(tensor_insert_stmt); |
| 342 | if (result == SQLITE_DONE101) |
| 343 | { |
| 344 | if (sqlite3_exec(conn, "RELEASE ccv_nnc_tensor_write", 0, 0, 0) != SQLITE_OK0) |
| 345 | { |
| 346 | if (workspace) |
| 347 | free(workspace); |
| 348 | return CCV_IO_ERROR; |
| 349 | } |
| 350 | } else { |
| 351 | sqlite3_exec(conn, "ROLLBACK TO ccv_nnc_tensor_write", 0, 0, 0); |
| 352 | sqlite3_exec(conn, "RELEASE ccv_nnc_tensor_write", 0, 0, 0); |
| 353 | } |
| 354 | if (workspace) |
| 355 | free(workspace); |
| 356 | return result == SQLITE_DONE101 ? CCV_IO_FINAL : CCV_IO_ERROR; |
| 357 | } |
| 358 | |
| 359 | int ccv_nnc_tensor_read(void* const handle, const char* const name, const ccv_nnc_tensor_io_option_t* const options, const int flags, const ccv_nnc_tensor_param_t* const tensor_params_optional, ccv_nnc_tensor_t** const tensor_out) |
| 360 | { |
| 361 | assert(name)((void) sizeof ((name) ? 1 : 0), __extension__ ({ if (name) ; else __assert_fail ("name", "ccv_nnc_tensor_io.c", 361, __extension__ __PRETTY_FUNCTION__); })); |
| 362 | sqlite3* conn = (sqlite3*)handle; |
| 363 | if (!conn) |
| 364 | return CCV_IO_ERROR; |
| 365 | const char tensor_select_qs[] = |
| 366 | "SELECT data, type, format, datatype, dim FROM tensors WHERE name=$name"; |
| 367 | sqlite3_stmt* tensor_select_stmt = 0; |
| 368 | if (SQLITE_OK0 != sqlite3_prepare_v2(conn, tensor_select_qs, sizeof(tensor_select_qs), &tensor_select_stmt, 0)) |
| 369 | return CCV_IO_ERROR; |
| 370 | sqlite3_bind_text(tensor_select_stmt, 1, name, -1, 0); |
| 371 | if (SQLITE_ROW100 != sqlite3_step(tensor_select_stmt)) |
| 372 | { |
| 373 | sqlite3_finalize(tensor_select_stmt); |
| 374 | return CCV_IO_ERROR; |
| 375 | } |
| 376 | ccv_nnc_tensor_t* tensor = *tensor_out; |
| 377 | ccv_nnc_tensor_param_t tensor_params; |
| 378 | int datatype = 0; |
| 379 | unsigned int identifier = 0; |
| 380 | const sqlite_int64 format_mix = sqlite3_column_int64(tensor_select_stmt, 2); |
| 381 | const int has_splits = !!(format_mix & CCV_NNC_TENSOR_IO_SPLIT_FORMAT(((sqlite_int64)1) << 32)); |
| 382 | if (!tensor) // If the tensor is not provided, we need to create one. |
| 383 | { |
| 384 | if (tensor_params_optional) |
| 385 | { |
| 386 | identifier = (sqlite3_column_int64(tensor_select_stmt, 1) >> 32) & 0xffffffff; |
| 387 | datatype = sqlite3_column_int64(tensor_select_stmt, 3) & 0xffffffff; |
| 388 | tensor_params = *tensor_params_optional; |
| 389 | assert(!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY))((void) sizeof ((!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY )) ? 1 : 0), __extension__ ({ if (!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY )) ; else __assert_fail ("!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY)" , "ccv_nnc_tensor_io.c", 389, __extension__ __PRETTY_FUNCTION__ ); })); |
| 390 | } else { |
| 391 | const sqlite_int64 type = sqlite3_column_int64(tensor_select_stmt, 1); |
| 392 | identifier = (type >> 32) & 0xffffffff; |
| 393 | tensor_params.type = (type & 0xffffffff); |
| 394 | tensor_params.format = (int)(format_mix & CCV_NNC_TENSOR_IO_FORMAT_MASK0xffffffffll); |
| 395 | const sqlite_int64 datatype_mix = sqlite3_column_int64(tensor_select_stmt, 3); |
| 396 | datatype = tensor_params.datatype = (datatype_mix & 0xffffffff); |
| 397 | tensor_params.reserved = (datatype_mix >> 32) & 0xffffffff; |
| 398 | const void* const dim = sqlite3_column_blob(tensor_select_stmt, 4); |
| 399 | memcpy(tensor_params.dim, dim, ccv_min(sizeof(tensor_params.dim), sqlite3_column_bytes(tensor_select_stmt, 4))({ typeof (sizeof(tensor_params.dim)) _a = (sizeof(tensor_params .dim)); typeof (sqlite3_column_bytes(tensor_select_stmt, 4)) _b = (sqlite3_column_bytes(tensor_select_stmt, 4)); (_a < _b ) ? _a : _b; })); |
| 400 | } |
| 401 | if (flags & CCV_NNC_TENSOR_READ_CPU_MEMORY) // Reset type to CPU memory. |
| 402 | tensor_params.type = (tensor_params.type & 0xfff00000) | CCV_TENSOR_CPU_MEMORY; |
| 403 | if (!options || !options->decode) |
| 404 | { |
| 405 | if (flags & CCV_NNC_TENSOR_READ_METADATA_ONLY) |
| 406 | { |
| 407 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, CCV_NO_DATA_ALLOC); // Set the data point to 1 so it is allocated without data. |
| 408 | assert(tensor->data.u8 == 0)((void) sizeof ((tensor->data.u8 == 0) ? 1 : 0), __extension__ ({ if (tensor->data.u8 == 0) ; else __assert_fail ("tensor->data.u8 == 0" , "ccv_nnc_tensor_io.c", 408, __extension__ __PRETTY_FUNCTION__ ); })); // Set it back to 0. |
| 409 | // Already done loading metadata, return. |
| 410 | sqlite3_reset(tensor_select_stmt); |
| 411 | sqlite3_clear_bindings(tensor_select_stmt); |
| 412 | sqlite3_finalize(tensor_select_stmt); |
| 413 | return CCV_IO_FINAL; |
| 414 | } else |
| 415 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 416 | } else { |
| 417 | assert(!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY))((void) sizeof ((!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY )) ? 1 : 0), __extension__ ({ if (!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY )) ; else __assert_fail ("!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY)" , "ccv_nnc_tensor_io.c", 417, __extension__ __PRETTY_FUNCTION__ ); })); |
| 418 | } |
| 419 | } else { |
| 420 | identifier = (sqlite3_column_int64(tensor_select_stmt, 1) >> 32) & 0xffffffff; |
| 421 | datatype = sqlite3_column_int64(tensor_select_stmt, 3) & 0xffffffff; |
| 422 | tensor_params = tensor->info; |
| 423 | assert(!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY))((void) sizeof ((!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY )) ? 1 : 0), __extension__ ({ if (!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY )) ; else __assert_fail ("!(flags & CCV_NNC_TENSOR_READ_METADATA_ONLY)" , "ccv_nnc_tensor_io.c", 423, __extension__ __PRETTY_FUNCTION__ ); })); |
| 424 | } |
| 425 | const void* data = sqlite3_column_blob(tensor_select_stmt, 0); |
| 426 | size_t data_bytes = sqlite3_column_bytes(tensor_select_stmt, 0); |
| 427 | unsigned char* split_workspace = 0; |
| 428 | if (has_splits) |
| 429 | { |
| 430 | const int split_result = _ccv_nnc_tensor_io_read_split_data(conn, name, data, data_bytes, &data, &data_bytes, &split_workspace); |
| 431 | if (split_result != SQLITE_OK0) |
| 432 | { |
| 433 | sqlite3_reset(tensor_select_stmt); |
| 434 | sqlite3_clear_bindings(tensor_select_stmt); |
| 435 | sqlite3_finalize(tensor_select_stmt); |
| 436 | return CCV_IO_ERROR; |
| 437 | } |
| 438 | } |
| 439 | int dim[CCV_NNC_MAX_DIM_ALLOC(12)]; |
| 440 | memcpy(dim, sqlite3_column_blob(tensor_select_stmt, 4), ccv_min(sizeof(dim), sqlite3_column_bytes(tensor_select_stmt, 4))({ typeof (sizeof(dim)) _a = (sizeof(dim)); typeof (sqlite3_column_bytes (tensor_select_stmt, 4)) _b = (sqlite3_column_bytes(tensor_select_stmt , 4)); (_a < _b) ? _a : _b; })); |
| 441 | const int nd = ccv_nnc_tensor_nd(dim); |
| 442 | if (datatype != tensor_params.datatype && CCV_GET_DATA_TYPE(tensor_params.datatype)((tensor_params.datatype) & 0xFF000) != CCV_QX) |
| 443 | { |
| 444 | // Only ever works for 16F to 32F or 32F to 16F transparently. |
| 445 | assert((datatype == CCV_16F && tensor_params.datatype == CCV_32F) || (datatype == CCV_32F && tensor_params.datatype == CCV_16F))((void) sizeof (((datatype == CCV_16F && tensor_params .datatype == CCV_32F) || (datatype == CCV_32F && tensor_params .datatype == CCV_16F)) ? 1 : 0), __extension__ ({ if ((datatype == CCV_16F && tensor_params.datatype == CCV_32F) || ( datatype == CCV_32F && tensor_params.datatype == CCV_16F )) ; else __assert_fail ("(datatype == CCV_16F && tensor_params.datatype == CCV_32F) || (datatype == CCV_32F && tensor_params.datatype == CCV_16F)" , "ccv_nnc_tensor_io.c", 445, __extension__ __PRETTY_FUNCTION__ ); })); |
| 446 | const size_t tensor_count = ccv_nnc_tensor_count(tensor_params); |
| 447 | ccv_nnc_tensor_param_t params = tensor_params; |
| 448 | params.datatype = datatype; |
| 449 | const size_t source_data_size = ccv_nnc_tensor_data_size(params); |
| 450 | #ifdef HAVE_CUDA1 |
| 451 | if (CCV_TENSOR_GET_MEMORY(tensor_params.type)((tensor_params.type) & 0x3) == CCV_TENSOR_GPU_MEMORY) |
| 452 | { |
| 453 | const size_t data_size = ccv_nnc_tensor_data_size(tensor_params); |
| 454 | unsigned char* workspace; |
| 455 | unsigned char* copying; |
| 456 | size_t decoded_size = data_size; |
| 457 | if (!options || !options->decode) |
| 458 | { |
| 459 | copying = workspace = ccmallocmalloc(data_size); |
| 460 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 461 | ccv_half_precision_to_float((uint16_t*)data, (float*)workspace, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 462 | else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) |
| 463 | ccv_float_to_half_precision((float*)data, (uint16_t*)workspace, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 464 | else |
| 465 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 465, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 466 | } else { |
| 467 | copying = workspace = ccmallocmalloc(data_size + source_data_size); |
| 468 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 469 | { |
| 470 | decoded_size = source_data_size; |
| 471 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace + data_size, &decoded_size)) |
| 472 | { |
| 473 | // If we loaded quantized tensor, don't do the conversion. |
| 474 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 475 | copying = workspace + data_size; |
| 476 | else { |
| 477 | ccv_half_precision_to_float((uint16_t*)(workspace + data_size), (float*)workspace, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 478 | decoded_size = data_size; |
| 479 | } |
| 480 | } else { |
| 481 | if (!tensor) |
| 482 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
Although the value stored to 'tensor' is used in the enclosing expression, the value is never actually read from 'tensor' | |
| 483 | ccv_half_precision_to_float((uint16_t*)data, (float*)workspace, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 484 | decoded_size = data_size; |
| 485 | } |
| 486 | } else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) { |
| 487 | decoded_size = source_data_size; |
| 488 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace + data_size, &decoded_size)) |
| 489 | { |
| 490 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 491 | copying = workspace + data_size; |
| 492 | else { |
| 493 | ccv_float_to_half_precision((float*)(workspace + data_size), (uint16_t*)workspace, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 494 | decoded_size = data_size; |
| 495 | } |
| 496 | } else { |
| 497 | if (!tensor) |
| 498 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 499 | ccv_float_to_half_precision((float*)data, (uint16_t*)workspace, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 500 | decoded_size = data_size; |
| 501 | } |
| 502 | } else |
| 503 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 503, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 504 | } |
| 505 | cumemcpy(tensor_out[0]->data.u8, tensor_out[0]->info.type, copying, CCV_TENSOR_CPU_MEMORY, decoded_size); |
| 506 | ccfreefree(workspace); |
| 507 | } else { |
| 508 | if (!options || !options->decode) |
| 509 | { |
| 510 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 511 | ccv_half_precision_to_float((uint16_t*)data, tensor->data.f32, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 512 | else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) |
| 513 | ccv_float_to_half_precision((float*)data, (uint16_t*)tensor->data.f16, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 514 | else |
| 515 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 515, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 516 | } else { |
| 517 | void* const workspace = ccmallocmalloc(source_data_size); |
| 518 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 519 | { |
| 520 | size_t decoded_size = source_data_size; |
| 521 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace, &decoded_size)) |
| 522 | { |
| 523 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 524 | { |
| 525 | if (decoded_size > 0) |
| 526 | memcpy(tensor_out[0]->data.f32, workspace, ccv_min(source_data_size, decoded_size)({ typeof (source_data_size) _a = (source_data_size); typeof ( decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; })); |
| 527 | } else |
| 528 | ccv_half_precision_to_float((uint16_t*)workspace, tensor_out[0]->data.f32, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 529 | } else { |
| 530 | if (!tensor) |
| 531 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 532 | ccv_half_precision_to_float((uint16_t*)data, tensor->data.f32, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 533 | } |
| 534 | } else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) { |
| 535 | size_t decoded_size = source_data_size; |
| 536 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace, &decoded_size)) |
| 537 | { |
| 538 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 539 | { |
| 540 | if (decoded_size > 0) |
| 541 | memcpy(tensor_out[0]->data.f16, workspace, ccv_min(source_data_size, decoded_size)({ typeof (source_data_size) _a = (source_data_size); typeof ( decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; })); |
| 542 | } else |
| 543 | ccv_float_to_half_precision((float*)workspace, (uint16_t*)tensor_out[0]->data.f16, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 544 | } else { |
| 545 | if (!tensor) |
| 546 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 547 | ccv_float_to_half_precision((float*)data, (uint16_t*)tensor->data.f16, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 548 | } |
| 549 | } else |
| 550 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 550, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 551 | ccfreefree(workspace); |
| 552 | } |
| 553 | } |
| 554 | #elif defined(HAVE_MPS) |
| 555 | if (CCV_TENSOR_GET_MEMORY(tensor_params.type)((tensor_params.type) & 0x3) == CCV_TENSOR_GPU_MEMORY) |
| 556 | { |
| 557 | const size_t data_size = ccv_nnc_tensor_data_size(tensor_params); |
| 558 | unsigned char* workspace; |
| 559 | unsigned char* copying; |
| 560 | size_t decoded_size = data_size; |
| 561 | if (!options || !options->decode) |
| 562 | { |
| 563 | copying = workspace = ccmallocmalloc(data_size); |
| 564 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 565 | ccv_half_precision_to_float((uint16_t*)data, (float*)workspace, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 566 | else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) |
| 567 | ccv_float_to_half_precision((float*)data, (uint16_t*)workspace, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 568 | else |
| 569 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 569, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 570 | } else { |
| 571 | copying = workspace = ccmallocmalloc(data_size + source_data_size); |
| 572 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 573 | { |
| 574 | decoded_size = source_data_size; |
| 575 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace + data_size, &decoded_size)) |
| 576 | { |
| 577 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 578 | copying = workspace + data_size; |
| 579 | else { |
| 580 | ccv_half_precision_to_float((uint16_t*)(workspace + data_size), (float*)workspace, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 581 | decoded_size = data_size; |
| 582 | } |
| 583 | } else { |
| 584 | if (!tensor) |
| 585 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 586 | ccv_half_precision_to_float((uint16_t*)data, (float*)workspace, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 587 | decoded_size = data_size; |
| 588 | } |
| 589 | } else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) { |
| 590 | decoded_size = source_data_size; |
| 591 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace + data_size, &decoded_size)) |
| 592 | { |
| 593 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 594 | copying = workspace + data_size; |
| 595 | else { |
| 596 | ccv_float_to_half_precision((float*)(workspace + data_size), (uint16_t*)workspace, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 597 | decoded_size = data_size; |
| 598 | } |
| 599 | } else { |
| 600 | if (!tensor) |
| 601 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 602 | ccv_float_to_half_precision((float*)data, (uint16_t*)workspace, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 603 | decoded_size = data_size; |
| 604 | } |
| 605 | } else |
| 606 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 606, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 607 | } |
| 608 | assert(tensor_out[0]->dataof == 0)((void) sizeof ((tensor_out[0]->dataof == 0) ? 1 : 0), __extension__ ({ if (tensor_out[0]->dataof == 0) ; else __assert_fail ( "tensor_out[0]->dataof == 0", "ccv_nnc_tensor_io.c", 608, __extension__ __PRETTY_FUNCTION__); })); |
| 609 | mpmemcpy(tensor_out[0]->data.u8, tensor_out[0]->dataof, tensor_out[0]->info.type, copying, 0, CCV_TENSOR_CPU_MEMORY, decoded_size); |
| 610 | ccfreefree(workspace); |
| 611 | } else { |
| 612 | if (!options || !options->decode) |
| 613 | { |
| 614 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 615 | ccv_half_precision_to_float((uint16_t*)data, tensor->data.f32, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 616 | else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) |
| 617 | ccv_float_to_half_precision((float*)data, (uint16_t*)tensor->data.f16, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 618 | else |
| 619 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 619, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 620 | } else { |
| 621 | void* const workspace = ccmallocmalloc(source_data_size); |
| 622 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 623 | { |
| 624 | size_t decoded_size = source_data_size; |
| 625 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace, &decoded_size)) |
| 626 | { |
| 627 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 628 | { |
| 629 | if (decoded_size > 0) |
| 630 | memcpy(tensor_out[0]->data.f32, workspace, ccv_min(source_data_size, decoded_size)({ typeof (source_data_size) _a = (source_data_size); typeof ( decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; })); |
| 631 | } else |
| 632 | ccv_half_precision_to_float((uint16_t*)workspace, tensor_out[0]->data.f32, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 633 | } else { |
| 634 | if (!tensor) |
| 635 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 636 | ccv_half_precision_to_float((uint16_t*)data, tensor->data.f32, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 637 | } |
| 638 | } else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) { |
| 639 | size_t decoded_size = source_data_size; |
| 640 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace, &decoded_size)) |
| 641 | { |
| 642 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 643 | { |
| 644 | if (decoded_size > 0) |
| 645 | memcpy(tensor_out[0]->data.f16, workspace, ccv_min(source_data_size, decoded_size)({ typeof (source_data_size) _a = (source_data_size); typeof ( decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; })); |
| 646 | } else |
| 647 | ccv_float_to_half_precision((float*)workspace, (uint16_t*)tensor_out[0]->data.f16, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 648 | } else { |
| 649 | if (!tensor) |
| 650 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 651 | ccv_float_to_half_precision((float*)data, (uint16_t*)tensor->data.f16, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 652 | } |
| 653 | } else |
| 654 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 654, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 655 | ccfreefree(workspace); |
| 656 | } |
| 657 | } |
| 658 | #else |
| 659 | if (!options || !options->decode) |
| 660 | { |
| 661 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 662 | ccv_half_precision_to_float((uint16_t*)data, tensor->data.f32, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 663 | else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) |
| 664 | ccv_float_to_half_precision((float*)data, (uint16_t*)tensor->data.f16, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 665 | else |
| 666 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 666, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 667 | } else { |
| 668 | void* const workspace = ccmallocmalloc(source_data_size); |
| 669 | if (datatype == CCV_16F && tensor_params.datatype == CCV_32F) |
| 670 | { |
| 671 | size_t decoded_size = source_data_size; |
| 672 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace, &decoded_size)) |
| 673 | { |
| 674 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 675 | { |
| 676 | if (decoded_size > 0) |
| 677 | memcpy(tensor_out[0]->data.f32, workspace, ccv_min(source_data_size, decoded_size)({ typeof (source_data_size) _a = (source_data_size); typeof ( decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; })); |
| 678 | } else |
| 679 | ccv_half_precision_to_float((uint16_t*)workspace, tensor_out[0]->data.f32, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 680 | } else { |
| 681 | if (!tensor) |
| 682 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 683 | ccv_half_precision_to_float((uint16_t*)data, tensor->data.f32, ccv_min(tensor_count, data_bytes / sizeof(uint16_t))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(uint16_t)) _b = (data_bytes / sizeof(uint16_t)); (_a < _b) ? _a : _b; })); |
| 684 | } |
| 685 | } else if (datatype == CCV_32F && tensor_params.datatype == CCV_16F) { |
| 686 | size_t decoded_size = source_data_size; |
| 687 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace, &decoded_size)) |
| 688 | { |
| 689 | if (CCV_GET_DATA_TYPE(tensor_out[0]->info.datatype)((tensor_out[0]->info.datatype) & 0xFF000) == CCV_QX) |
| 690 | { |
| 691 | if (decoded_size > 0) |
| 692 | memcpy(tensor_out[0]->data.f16, workspace, ccv_min(source_data_size, decoded_size)({ typeof (source_data_size) _a = (source_data_size); typeof ( decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; })); |
| 693 | } else |
| 694 | ccv_float_to_half_precision((float*)workspace, (uint16_t*)tensor_out[0]->data.f16, ccv_min(tensor_count, ccv_min(source_data_size, decoded_size) / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (({ typeof (source_data_size) _a = (source_data_size); typeof (decoded_size ) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float )) _b = (({ typeof (source_data_size) _a = (source_data_size) ; typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; }) / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 695 | } else { |
| 696 | if (!tensor) |
| 697 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 698 | ccv_float_to_half_precision((float*)data, (uint16_t*)tensor->data.f16, ccv_min(tensor_count, data_bytes / sizeof(float))({ typeof (tensor_count) _a = (tensor_count); typeof (data_bytes / sizeof(float)) _b = (data_bytes / sizeof(float)); (_a < _b) ? _a : _b; })); |
| 699 | } |
| 700 | } else |
| 701 | { assert(0)((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "ccv_nnc_tensor_io.c", 701, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 702 | ccfreefree(workspace); |
| 703 | } |
| 704 | #endif |
| 705 | } else { |
| 706 | // If it is QX, we need to have a custom decoder to decode properly. |
| 707 | if (datatype != tensor_params.datatype) |
| 708 | { assert(options && options->decode)((void) sizeof ((options && options->decode) ? 1 : 0), __extension__ ({ if (options && options->decode ) ; else __assert_fail ("options && options->decode" , "ccv_nnc_tensor_io.c", 708, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 709 | size_t data_size = ccv_nnc_tensor_data_size(tensor_params); |
| 710 | #ifdef HAVE_CUDA1 |
| 711 | if (!options || !options->decode) |
| 712 | { |
| 713 | if (CCV_TENSOR_GET_MEMORY(tensor_params.type)((tensor_params.type) & 0x3) == CCV_TENSOR_GPU_MEMORY) |
| 714 | cumemcpy(tensor->data.u8, tensor->info.type, data, CCV_TENSOR_CPU_MEMORY, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 715 | else |
| 716 | memcpy(tensor->data.u8, data, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 717 | } else { |
| 718 | if (CCV_TENSOR_GET_MEMORY(tensor_params.type)((tensor_params.type) & 0x3) == CCV_TENSOR_GPU_MEMORY) |
| 719 | { |
| 720 | void* const workspace = ccmallocmalloc(data_size); |
| 721 | size_t decoded_size = data_size; |
| 722 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace, &decoded_size)) |
| 723 | cumemcpy(tensor_out[0]->data.u8, tensor_out[0]->info.type, workspace, CCV_TENSOR_CPU_MEMORY, ccv_min(data_size, decoded_size)({ typeof (data_size) _a = (data_size); typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; })); |
| 724 | else { |
| 725 | if (!tensor) |
| 726 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 727 | cumemcpy(tensor->data.u8, tensor->info.type, data, CCV_TENSOR_CPU_MEMORY, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 728 | } |
| 729 | ccfreefree(workspace); |
| 730 | } else { |
| 731 | size_t decoded_size = data_size; |
| 732 | if (!options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, tensor ? tensor->data.u8 : 0, &decoded_size)) |
| 733 | { |
| 734 | if (!tensor) |
| 735 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 736 | memcpy(tensor->data.u8, data, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 737 | } |
| 738 | } |
| 739 | } |
| 740 | #elif defined(HAVE_MPS) |
| 741 | if (!options || !options->decode) |
| 742 | { |
| 743 | if (CCV_TENSOR_GET_MEMORY(tensor_params.type)((tensor_params.type) & 0x3) == CCV_TENSOR_GPU_MEMORY) |
| 744 | { |
| 745 | assert(tensor->dataof == 0)((void) sizeof ((tensor->dataof == 0) ? 1 : 0), __extension__ ({ if (tensor->dataof == 0) ; else __assert_fail ("tensor->dataof == 0" , "ccv_nnc_tensor_io.c", 745, __extension__ __PRETTY_FUNCTION__ ); })); |
| 746 | mpmemcpy(tensor->data.u8, tensor->dataof, tensor->info.type, data, 0, CCV_TENSOR_CPU_MEMORY, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 747 | } else |
| 748 | memcpy(tensor->data.u8, data, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 749 | } else { |
| 750 | if (CCV_TENSOR_GET_MEMORY(tensor_params.type)((tensor_params.type) & 0x3) == CCV_TENSOR_GPU_MEMORY) |
| 751 | { |
| 752 | if (tensor) |
| 753 | { assert(tensor->dataof == 0)((void) sizeof ((tensor->dataof == 0) ? 1 : 0), __extension__ ({ if (tensor->dataof == 0) ; else __assert_fail ("tensor->dataof == 0" , "ccv_nnc_tensor_io.c", 753, __extension__ __PRETTY_FUNCTION__ ); })); } |
| 754 | void* const workspace = ccmallocmalloc(data_size); |
| 755 | size_t decoded_size = data_size; |
| 756 | if (options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, workspace, &decoded_size)) { |
| 757 | mpmemcpy(tensor_out[0]->data.u8, tensor_out[0]->dataof, tensor_out[0]->info.type, workspace, 0, CCV_TENSOR_CPU_MEMORY, ccv_min(data_size, decoded_size)({ typeof (data_size) _a = (data_size); typeof (decoded_size) _b = (decoded_size); (_a < _b) ? _a : _b; })); |
| 758 | } else { |
| 759 | if (!tensor) |
| 760 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 761 | mpmemcpy(tensor->data.u8, tensor->dataof, tensor->info.type, data, 0, CCV_TENSOR_CPU_MEMORY, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 762 | } |
| 763 | ccfreefree(workspace); |
| 764 | } else { |
| 765 | size_t decoded_size = data_size; |
| 766 | if (!options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, tensor ? tensor->data.u8 : 0, &decoded_size)) |
| 767 | { |
| 768 | if (!tensor) |
| 769 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 770 | memcpy(tensor->data.u8, data, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 771 | } |
| 772 | } |
| 773 | } |
| 774 | #else |
| 775 | if (!options || !options->decode) |
| 776 | memcpy(tensor->data.u8, data, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 777 | else { |
| 778 | size_t decoded_size = data_size; |
| 779 | if (!options->decode(data, data_bytes, datatype, dim, nd, identifier, options->context, tensor_params, tensor_out, tensor ? tensor->data.u8 : 0, &decoded_size)) |
| 780 | { |
| 781 | if (!tensor) |
| 782 | *tensor_out = tensor = ccv_nnc_tensor_new(0, tensor_params, 0); |
| 783 | memcpy(tensor->data.u8, data, ccv_min(data_size, data_bytes)({ typeof (data_size) _a = (data_size); typeof (data_bytes) _b = (data_bytes); (_a < _b) ? _a : _b; })); |
| 784 | } |
| 785 | } |
| 786 | #endif |
| 787 | } |
| 788 | tensor_out[0]->type &= ~CCV_GARBAGE; // If it is marked as garbage, remove that mark now. |
| 789 | sqlite3_reset(tensor_select_stmt); |
| 790 | sqlite3_clear_bindings(tensor_select_stmt); |
| 791 | sqlite3_finalize(tensor_select_stmt); |
| 792 | if (split_workspace) |
| 793 | ccfreefree(split_workspace); |
| 794 | return CCV_IO_FINAL; |
| 795 | } |