1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
/* Copyright (c) 2008-2022 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Covered Software is provided under this License on an "as is"
* basis, without warranty of any kind, either expressed, implied, or
* statutory, including, without limitation, warranties that the
* Covered Software is free of defects, merchantable, fit for a
* particular purpose or non-infringing.
* See the Mozilla Public License v. 2.0 for more details.
*
* For more details, see http://www.mrtrix.org/.
*/
#include "header.h"
#include "image_io/sparse.h"
namespace MR
{
namespace ImageIO
{
void SparseLegacy::load (const Header& header, size_t)
{
Default::load (header,0);
std::fstream stream (file.name.c_str(), std::ios_base::in | std::ios_base::binary);
stream.seekg (0, std::ios::end);
const uint64_t file_size = stream.tellg();
stream.close();
const uint64_t current_sparse_data_size = file_size - file.start;
if (current_sparse_data_size) {
mmap.reset (new File::MMap (file, is_image_readwrite(), true, current_sparse_data_size));
data_end = current_sparse_data_size;
} else if (is_image_readwrite()) {
//CONF option: SparseDataInitialSize
//CONF default: 16777216
//CONF Initial buffer size for data in MRtrix sparse image format file (in bytes).
// Default = initialise 16MB, this is enough to store whole-brain fixel data at 2.5mm resolution
const uint64_t init_sparse_data_size = File::Config::get_int ("SparseDataInitialSize", 16777216);
const size_t new_file_size = file.start + init_sparse_data_size;
DEBUG ("Initialising output sparse data file " + file.name + ": new file size " + str(new_file_size)
+ " (" + str(init_sparse_data_size) + " of which is initial sparse data buffer)");
File::resize (file.name, new_file_size);
mmap.reset (new File::MMap (file, is_image_readwrite(), false, init_sparse_data_size));
// Writes a single uint32_t(0) to the start of the sparse data region
// Any voxel that has its value initialised to 0 will point here, and therefore dereferencing of any
// such voxel will yield a Fixel::Value with zero elements
memset (off2mem (0), 0x00, sizeof (uint32_t));
data_end = sizeof(uint32_t);
}
// If this is the formation of a new image, want to explicitly zero all of the
// raw image data - otherwise any random data could be misinterpreted as a large
// pointer offset from the start of the sparse image data
if (is_image_new()) {
for (auto& i : mmaps)
memset (i->address(), 0x00, i->size());
}
}
void SparseLegacy::unload (const Header& header)
{
Default::unload (header);
const uint64_t truncate_file_size = (data_end == size()) ? 0 : file.start + data_end;
// Null the excess data before closing the memory map to prevent std::ofstream from giving an error
// (writing uninitialised values)
memset (off2mem(data_end), 0x00, size() - data_end);
mmap.reset();
if (truncate_file_size) {
DEBUG ("truncating sparse image data file " + file.name + " to " + str(truncate_file_size) + " bytes");
File::resize (file.name, truncate_file_size);
}
}
uint64_t SparseLegacy::set_numel (const uint64_t old_offset, const uint32_t numel)
{
assert (is_image_readwrite());
// Before allocating new memory, verify that the current offset does not point to
// a voxel that has more elements than is required
if (old_offset) {
assert (old_offset < data_end);
const uint32_t existing_numel = get_numel (old_offset);
if (existing_numel >= numel) {
// Set the new number of elements, clear the unwanted data, and return
memcpy (off2mem(old_offset), &numel, sizeof(uint32_t));
memset (off2mem(old_offset) + sizeof(uint32_t) + (numel * class_size), 0x00, ((existing_numel - numel) * class_size));
// If this voxel is being cleared (i.e. no elements), rather than returning a pointer to a voxel with
// zero elements, instead return a pointer to the start of the sparse data, where there's a single
// uint32_t(0) which can be used for any and all voxels with zero elements
return numel ? old_offset : 0;
} else {
// Existing memory allocation for this voxel is not sufficient; erase it
// Note that the handler makes no attempt at re-using this memory later; new data is just concatenated
// to the end of the buffer
memset (off2mem(old_offset), 0x00, sizeof(uint32_t) + (existing_numel * class_size));
}
}
// Don't allocate memory for voxels with no sparse data; just point them all to the start of the
// sparse data where theres a single uint32_t(0)
if (!numel)
return 0;
const int64_t requested_size = sizeof (uint32_t) + (numel * class_size);
if (data_end + requested_size > size()) {
// size() should never be empty if data is being written...
assert (size());
uint64_t new_sparse_data_size = 2 * size();
// Cover rare case where a huge memory request occurs early
while (new_sparse_data_size < data_end + requested_size)
new_sparse_data_size *= 2;
// Memory error arises due to the uninitialised data between the end of the sparse data and the end
// of the file at its current size being written using std::ofstream
// Therefore, explicitly null all data that hasn't already been written
// (this does not prohibit the use of this memory for subsequent sparse data though)
memset (off2mem(data_end), 0x00, size() - data_end);
mmap.reset();
const size_t new_file_size = file.start + new_sparse_data_size;
DEBUG ("Resizing sparse data file " + file.name + ": new file size " + str(new_file_size) + " (" + str(new_sparse_data_size) + " of which is for sparse data)");
File::resize (file.name, new_file_size);
mmap.reset (new File::MMap (file, Base::writable, true, new_sparse_data_size));
}
// Write the uint32_t indicating the number of elements in this voxel
memcpy (off2mem(data_end), &numel, sizeof(uint32_t));
// The return value is the offset from the beginning of the sparse data
const uint64_t ret = data_end;
data_end += requested_size;
return ret;
}
}
}
|