1 module dhdf5.dataset; 2 3 private 4 { 5 import std.traits : isDynamicArray; 6 import std.range : isInputRange; 7 import dhdf5.dataspec : DataSpecification; 8 9 /// array is dynamic array whose dimensions sizes are set using dim array elements 10 /// as sizes. 11 auto setDynArrayDimensions(T)(ref T arr, const size_t[] dim) if(isDynamicArray!T) 12 { 13 auto setDimImpl(T)(ref T t, const size_t[] dim) 14 { 15 static if(isDynamicArray!T) 16 { 17 static if(isDynamicArray!(typeof(t[0]))) 18 { 19 t.length = dim[0]; 20 foreach(ref e; t[0..$]) 21 { 22 e.length = dim[1]; 23 } 24 25 return setDimImpl(t[0], dim[1..$]); 26 } 27 else 28 { 29 t.length = dim[0]; 30 } 31 } 32 33 } 34 35 return setDimImpl(arr, dim); 36 } 37 38 unittest 39 { 40 size_t[] dim = [1, 3, 5]; 41 int[][][] res; 42 setDynArrayDimensions(res, dim); 43 assert(res.length == 1); 44 assert(res[0].length == 3); 45 assert(res[0][0].length == 5); 46 47 int[] res2; 48 setDynArrayDimensions(res2, dim); 49 assert(res2.length == 1); 50 } 51 52 template rankOf(R) 53 if (isInputRange!R) 54 { 55 auto rankOfImpl(Range)() 56 { 57 import std.range : ElementType; 58 59 static if (isInputRange!Range) 60 { 61 return 1 + rankOfImpl!(ElementType!Range); 62 } 63 else 64 return 0; 65 } 66 67 enum rankOf = rankOfImpl!R; 68 } 69 70 unittest 71 { 72 { 73 enum rank = rankOf!(uint[]); 74 static assert (rank == 1); 75 } 76 77 { 78 enum rank = rankOf!(uint[][][][][][]); 79 static assert (rank == 6); 80 } 81 82 { 83 import std.range : only; 84 85 auto range = only(1, 2); 86 alias Range = typeof(range); 87 88 enum rank = rankOf!Range; 89 static assert (rank == 1); 90 } 91 92 { 93 import std.range : only; 94 95 auto range = only(1, 2); 96 alias Range = typeof(range); 97 98 auto ror = only(range, range); 99 alias RoR = typeof(ror); 100 101 enum rank = rankOf!RoR; 102 static assert (rank == 2); 103 } 104 } 105 106 struct Dataspace 107 { 108 import hdf5.hdf5 : hid_t, H5Dget_space, H5Sclose; 109 110 hid_t hid = -1; 111 112 this(hid_t dataset) 113 { 114 hid = H5Dget_space (dataset); 115 assert(hid >= 0); 116 } 117 118 ~this() 119 { 120 if (hid != -1) 121 { 122 H5Sclose (hid); 123 hid = -1; 124 } 125 } 126 127 alias hid this; 128 } 129 } 130 131 struct Dataset(Data, DataSpecType = typeof(DataSpecification!Data.make())) 132 if (isInputRange!Data) 133 { 134 import std.range : ElementType, hasLength; 135 import std.traits : isImplicitlyConvertible; 136 import std.typecons : RefCounted; 137 import hdf5.hdf5 : hid_t, hsize_t, H5Sget_simple_extent_ndims, H5Dclose; 138 import dhdf5.file : H5File; 139 140 static assert (isInputRange!Data, Data.stringof ~ " should be input range"); 141 142 enum rank = rankOf!Data; 143 alias Type = RefCounted!(Dataset!(Data, DataSpecType)); 144 145 public 146 { 147 this(hid_t dataset, DataSpecType data_spec) 148 { 149 import std.exception : enforce; 150 enforce (dataset != -1); 151 _dataset = dataset; 152 _data_spec = data_spec; 153 154 debug 155 { 156 import hdf5.hdf5 : H5Sget_simple_extent_ndims; 157 158 auto space_id = Dataspace (_dataset); 159 assert (rank == H5Sget_simple_extent_ndims (space_id)); 160 } 161 } 162 163 ~this() 164 { 165 import hdf5.hdf5 : H5Dclose; 166 167 if (_dataset != -1) 168 { 169 H5Dclose(_dataset); 170 _dataset = -1; 171 } 172 } 173 } 174 175 static create(ref const(H5File) file, string name) 176 { 177 import std..string: toStringz; 178 import hdf5.hdf5 : H5P_DEFAULT, H5P_DATASET_CREATE, H5Pcreate, H5Pclose, 179 H5Pset_chunk, H5Screate_simple, H5Dcreate2; 180 import dhdf5.dataspec : countDimensions; 181 import std.conv: castFrom; 182 183 enum DEFAULT_CHUNK_SIZE = 512; 184 auto dcpl_id = H5P_DEFAULT; 185 186 /* Create a dataset creation property list and set it to use chunking 187 */ 188 auto max_dim = countDimensions!(Data)(); 189 auto curr_dim = max_dim.dup; 190 curr_dim[] = 0; 191 hsize_t[] chunk_dims; 192 chunk_dims.length = curr_dim.length; 193 chunk_dims[] = DEFAULT_CHUNK_SIZE; 194 dcpl_id = H5Pcreate (H5P_DATASET_CREATE); 195 scope(exit) H5Pclose (dcpl_id); 196 197 H5Pset_chunk (dcpl_id, castFrom!size_t.to!int(chunk_dims.length), chunk_dims.ptr); 198 199 auto space = H5Screate_simple (castFrom!(size_t).to!int(curr_dim.length), curr_dim.ptr, max_dim.ptr); 200 auto data_spec = DataSpecType.make(); 201 auto dataset = H5Dcreate2 (file.tid, name.toStringz, data_spec.tid, space, H5P_DEFAULT, dcpl_id, H5P_DEFAULT); 202 assert(dataset >= 0); 203 return RefCounted!(Dataset!(Data, DataSpecType))(dataset, data_spec); 204 } 205 206 static open(ref const(H5File) file, string name) 207 { 208 import std..string: toStringz; 209 import hdf5.hdf5 : H5P_DEFAULT, H5Dopen2; 210 211 auto data_spec = DataSpecType.make(); 212 auto dataset = H5Dopen2 (file.tid, name.toStringz, H5P_DEFAULT); 213 assert(dataset >= 0); 214 return RefCounted!(Dataset!(Data, DataSpecType))(dataset, data_spec); 215 } 216 217 /** 218 * Return current shape, that can change during programm running. 219 */ 220 auto currShape() const 221 { 222 import hdf5.hdf5 : H5Sget_simple_extent_dims; 223 224 if (_dataset == -1) 225 return typeof(_curr_shape).init; 226 227 auto space_id = Dataspace (_dataset); 228 229 H5Sget_simple_extent_dims (space_id, _curr_shape.ptr, _max_shape.ptr); 230 231 return _curr_shape; 232 } 233 234 auto currShape(hsize_t[] extent) 235 { 236 import hdf5.hdf5; 237 238 assert (_dataset != -1); 239 auto status = H5Dset_extent (_dataset, extent.ptr); 240 assert(status >= 0); 241 } 242 243 /** 244 * Return maximal shape, that doesn't change during program running. 245 */ 246 auto maxShape() const pure 247 { 248 return _max_shape; 249 } 250 251 /* 252 * Read data from the dataset. 253 */ 254 auto read() const 255 { 256 import hdf5.hdf5; 257 258 ElementType!Data[] data; 259 auto filespace = Dataspace(_dataset); 260 261 // get current size 262 auto offset = currShape().dup; 263 // set offset to zero for all dimensions 264 offset[] = 0; 265 herr_t status; 266 267 setDynArrayDimensions(data, currShape()); 268 /* 269 * Define the memory space to read dataset. 270 */ 271 auto memspace = H5Screate_simple(rank, currShape().ptr, null); 272 scope(exit) H5Sclose(memspace); 273 274 /* 275 * Read dataset 276 */ 277 status = H5Dread(_dataset, _data_spec.tid, memspace, filespace, 278 H5P_DEFAULT, data.ptr); 279 assert(status >= 0); 280 281 return data; 282 } 283 284 /* 285 * Read data from the dataset. 286 */ 287 auto read(ElementType!Data[] data) const 288 { 289 import hdf5.hdf5; 290 291 if (data.length < currShape[0]) 292 return null; 293 294 auto filespace = Dataspace(_dataset); 295 296 // get current size 297 auto offset = currShape().dup; 298 // set offset to zero for all dimensions 299 offset[] = 0; 300 herr_t status; 301 302 /* 303 * Define the memory space to read dataset. 304 */ 305 auto memspace = H5Screate_simple(rank, currShape().ptr, null); 306 scope(exit) H5Sclose(memspace); 307 308 /* 309 * Read dataset 310 */ 311 status = H5Dread(_dataset, _data_spec.tid, memspace, filespace, 312 H5P_DEFAULT, data.ptr); 313 assert(status >= 0); 314 315 return data; 316 } 317 318 /** 319 * Read count data from file starting with offset 320 */ 321 auto read(hsize_t[] offset, hsize_t[] count) const 322 { 323 import hdf5.hdf5 : H5Sselect_hyperslab, H5Screate_simple, H5Dread, H5Sclose, 324 H5S_seloper_t, H5P_DEFAULT; 325 326 //assert((offset+count) <= _max_shape[0]); 327 /* 328 * get the file dataspace. 329 */ 330 auto dataspace = Dataspace (_dataset); // dataspace identifier 331 332 auto status = H5Sselect_hyperslab (dataspace, H5S_seloper_t.H5S_SELECT_SET, offset.ptr, null, count.ptr, null); 333 assert(status >= 0); 334 /* 335 * Define memory dataspace. 336 */ 337 auto mem_offset = offset; 338 mem_offset[] = 0; 339 auto mem_count = count; 340 auto dimsm = mem_count; 341 auto memspace = H5Screate_simple (rank, dimsm.ptr, null); 342 scope(exit) H5Sclose (memspace); 343 344 /* 345 * Define memory hyperslab. 346 */ 347 status = H5Sselect_hyperslab (memspace, H5S_seloper_t.H5S_SELECT_SET, mem_offset.ptr, null, mem_count.ptr, null); 348 assert(status >=0); 349 350 ElementType!Data[] data; 351 setDynArrayDimensions (data, count); 352 353 status = H5Dread (_dataset, _data_spec.tid, memspace, dataspace, H5P_DEFAULT, data.ptr); 354 assert(status >= 0); 355 return data; 356 } 357 358 void write(Range, Args...)(Range range, Args args) 359 if (isImplicitlyConvertible!(ElementType!Range, ElementType!Data) && 360 !is(Range == ElementType!(Data)[])) 361 { 362 import std.container : Array; 363 364 auto buffer = Array!(ElementType!Data)(range); 365 366 write((&buffer[0])[0..buffer.length], args); 367 } 368 369 /* 370 * Write data to the dataset 371 */ 372 auto write(ElementType!Data[] data) 373 { 374 hsize_t[rank] offset; 375 offset[] = 0; 376 write(data, offset[]); 377 } 378 379 /* 380 * Write data to the dataset; 381 */ 382 auto write(ElementType!Data[] data, const(hsize_t)[] offset) 383 { 384 import std.exception : enforce; 385 enforce (data.length+offset[0] <= currShape[0], "Bounds checking failed!"); 386 387 import hdf5.hdf5 : herr_t, H5Sselect_hyperslab, H5Screate_simple, H5Dwrite, 388 H5S_seloper_t, H5P_DEFAULT, H5Sclose; 389 390 assert(offset.length == rank); 391 hsize_t[rank] count = [data.length]; 392 assert( count.length == rank); 393 herr_t status; 394 395 auto filespace = Dataspace (_dataset); 396 status = H5Sselect_hyperslab (filespace, H5S_seloper_t.H5S_SELECT_SET, offset.ptr, null, count.ptr, null); 397 assert(status >= 0); 398 399 auto memspace = H5Screate_simple (rank, count.ptr, null); 400 scope(exit) H5Sclose (memspace); 401 402 status = H5Dwrite (_dataset, _data_spec.tid, memspace, filespace, H5P_DEFAULT, data.ptr); 403 assert(status >= 0); 404 } 405 406 auto remove(hsize_t idx) 407 { 408 auto l = currShape[0]; 409 if (idx != l-1) 410 { 411 auto buffer = read([idx+1], [currShape[0]-idx-1]); 412 write(buffer, [idx]); 413 } 414 currShape = [l - 1]; 415 } 416 417 auto remove(IndexRange)(IndexRange index_range) 418 { 419 420 } 421 422 Range opSlice() 423 { 424 return Range(this, 0, currShape[0]); 425 } 426 427 auto add(ElementType!Data element) 428 { 429 // set new shape 430 auto last_index = currShape[0]; 431 currShape = [last_index+1]; 432 write([element], [last_index]); 433 } 434 435 auto add(Range)(Range range) 436 if (is(ElementType!Range : ElementType!Data) && hasLength!Range) 437 { 438 // set new shape 439 auto last_index = currShape[0]; 440 currShape = [last_index+range.length]; 441 write(range, [last_index]); 442 } 443 444 struct Range 445 { 446 // index of starting element + element count of the range 447 private 448 { 449 size_t _start, _length; 450 Dataset* _dataset; 451 } 452 453 @disable 454 this(); 455 456 this(ref Dataset dataset, size_t start, size_t length) 457 { 458 _dataset = &dataset; 459 _start = start; 460 _length = length; 461 } 462 463 bool empty() const 464 { 465 return _length == 0; 466 } 467 468 ref ElementType!Data front() const 469 { 470 return (*_dataset)[_start]; 471 } 472 473 ref ElementType!Data back() const 474 { 475 return (*_dataset)[_start+_length-1]; 476 } 477 478 void popFront() 479 { 480 import std.exception : enforce; 481 482 enforce (!empty); 483 484 _start++; 485 _length--; 486 } 487 488 void popBack() 489 { 490 import std.exception : enforce; 491 492 enforce (!empty); 493 494 _length--; 495 } 496 497 auto save() const 498 { 499 return this; 500 } 501 502 ref auto opIndex(size_t index) const 503 { 504 return (*_dataset)[index]; 505 } 506 507 size_t length() const 508 { 509 return _length; 510 } 511 } 512 513 ref ElementType!Data opIndex(size_t index) const 514 { 515 if (index >= currShape[0]) 516 throw new Exception("Bounds"); 517 518 return read([index], [1])[0]; 519 } 520 521 auto opIndexAssign(ElementType!Data element, size_t index) 522 { 523 write([element], [index]); 524 } 525 526 auto opOpAssign(string op)(ElementType!Data rhs) 527 { 528 static if (op == "~") 529 { 530 add(rhs); 531 } 532 } 533 534 auto opOpAssign(string op, Range)(Range r) 535 { 536 static if (op == "~") 537 { 538 add(r); 539 } 540 } 541 542 auto tid() 543 { 544 return _dataset; 545 } 546 547 private: 548 hid_t _dataset = -1; 549 DataSpecType _data_spec; 550 // Current shape of dataset 551 hsize_t[rank] _curr_shape; 552 // Maximal shape of dataset 553 hsize_t[rank] _max_shape; 554 }