Skip to content

Repository files navigation

 ╦ ╦┬ ┬┌─┐┌─┐┬─┐ ╔╗ ┬ ┬┌─┐┌─┐┌─┐┬─┐
╠═╣└┬┘├─┘├┤ ├┬┘ ╠╩╗│ │├┤ ├┤ ├┤ ├┬┘
╩ ╩ ┴ ┴ └─┘┴└─ ╚═╝└─┘└ └ └─┘┴└─

A C++ structure to manage multi-dimensional data efficiently and safely

This container was designed to hold dynamically-allocated N-dimensional datasets in memory and provide convenient access to it, while minimizing performance/memory overhead as well as using a single allocation for all the data.

Usage Example

HyperBuffer<float, 2> buffer2D (2, 5);
float element01 = buffer2D[0][1];
float element14 = buffer2D.at(1, 4); // alternative to []float** rawPointer = buffer2D.data();
float* outerDimension0 = buffer2D[0];
HyperBufferView<float, 1> subView = buffer2D.subView(1);
float* outerDimension1 = subView.data();
// Any number of dimensions
HyperBuffer<int, 8> buffer8D (3, 4, 3, 1, 6, 256, 11, 7); // Wrapper for existing multi-dimensional data (zero dynamic memory allocation!)float bufferL[]{ 0.1f, 0.2f, 0.3f }; float bufferR[]{ -0.1f, -0.2f, -0.3f };
float* stereoBuffer[2] = { bufferL, bufferR };
HyperBufferViewNC<float, 2> wrapper(stereoBuffer, 2, 3);
wrapper[0][1] = 0.22f; // access left channel, second sample

Requirements / Compatibility

  • C++14, STL only
  • Compiled & Tested with:
    • Linux / macos / Windwos
    • GCC, Clang and MSVC
    • x86_64 and arm64 architectures

Design paradigms:

HyperBuffer is designed as a multi-dimensional counterpart to std::array and/or std::vector. However, it differs from said standard library classes on the 'points of commitment', i.e. the point in time at which certain parameters have to be specified (and cannot be changed afterwards):

HyperBufferstd::arraystd::vector
element data type (T)compile-timecompile-timecompile-time
number of dimensions (N)compile-time= 1= 1
extent of dimensionsconstruction-timecompile-timerun-time

HyperBuffer is thus a non-resizable container like std::array, however in contrast, the extent of the dimensions can be specified at runtime.

Note: For the time being, dimensions are constrained to be uniform, i.e. each 'slice' in a given dimension has equal length and data type.

Design choices were carefully weighed with the following prime directive in mind: avoid dynamic memory allocation as much as possible. This is crucial in realtime environments with a strict need for deterministic behaviour (e.g. audio processing threads).

Thanks to the chosen memory model, dynamic memory allocation happens only during construction. Furthermore, the entire data and pointer memory is each allocated in a single call (cf. documentation in Design Details), thereby avoiding memory fragmentation / churn.

Data Storage & Ownership Variants

HyperBuffer comes in 3 incarnations that use different levels of ownership on the data. In multi-dimensional structures, we can differentiate between the memory required to store the pointers.

ownershipuse case
HyperBufferowns/allocates pointers & dataStoring multi-dimensional data and providing a simple and safe API to it.
HyperBufferViewowns pointers, externally-allocated dataView for existing data in the HyperBuffer memory format (contiguous 1D memory) - e.g. a view to a sub-dimension of HyperBuffer
HyperBufferViewNCexternally-allocated pointers & dataWrapper for existing multi-dimensional data (non-contiguous memory, e.g. float**); gives it the same API as HyperBuffer

Note: Behaviour on copy & move: HyperBuffer copies/moves the data like a normal object with data ownership. When copying HyperBufferViewNC and HyperBufferView, however, the data is not duplicated - the copy references the original data as well.

API features & Memory Management:

In addition to information about the geometry (dimensions and extent thereof), the API has several ways of accessing data:

functiondescriptionreturn valueHyperBufferHyperBufferViewHyperBufferViewNC
.data()access the start of highest dimension of the dataraw pointer (e.g. float***)non-allocatingnon-allocatingnon-allocating
operator[.]access the N-1 sub-dimension at the given index; can be chained: h[3][0][6]raw pointer (e.g. float**); data value if N==1non-allocatingnon-allocatingnon-allocating
at(...)access data in lowest dimension (N arguments)data value (e.g. float)allocatingallocatingnon-allocating
subView(...)access data in any dimension (variable-length argument)N-x view to the dataallocatingallocatingnon-allocating

While HyperBufferViewNC never allocates memory under any circumstances, you can see above that the .at() and subView()accessors allocate dynamic memory for the other variants. This is because a new N-1 HyperBufferView is constructed, which allocates memory for the pointers.

Further guarantees:

  • accessing data is always allocation-free
  • dynamic allocation-free move() semantics
  • (planned) alignment of the data (lowest-order/innermost dimension) can be specified ('owning' mode only)

Build Status / Quality Metrics

Build & Test HyperBufferBuild & Test HyperBuffer

Quality Gate Status

BugsCode SmellsCoverageDuplicated Lines (%)

About

A C++ structure to manage multi-dimensional data efficiently and safely

Topics

Resources

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - Sidelobe/HyperBuffer: A C++ structure to manage multi-dimensional data efficiently and safely · GitHub
Skip to content

Repository files navigation

 ╦ ╦┬ ┬┌─┐┌─┐┬─┐ ╔╗ ┬ ┬┌─┐┌─┐┌─┐┬─┐
╠═╣└┬┘├─┘├┤ ├┬┘ ╠╩╗│ │├┤ ├┤ ├┤ ├┬┘
╩ ╩ ┴ ┴ └─┘┴└─ ╚═╝└─┘└ └ └─┘┴└─

A C++ structure to manage multi-dimensional data efficiently and safely

This container was designed to hold dynamically-allocated N-dimensional datasets in memory and provide convenient access to it, while minimizing performance/memory overhead as well as using a single allocation for all the data.

Usage Example

HyperBuffer<float, 2> buffer2D (2, 5);
float element01 = buffer2D[0][1];
float element14 = buffer2D.at(1, 4); // alternative to []float** rawPointer = buffer2D.data();
float* outerDimension0 = buffer2D[0];
HyperBufferView<float, 1> subView = buffer2D.subView(1);
float* outerDimension1 = subView.data();
// Any number of dimensions
HyperBuffer<int, 8> buffer8D (3, 4, 3, 1, 6, 256, 11, 7); // Wrapper for existing multi-dimensional data (zero dynamic memory allocation!)float bufferL[]{ 0.1f, 0.2f, 0.3f }; float bufferR[]{ -0.1f, -0.2f, -0.3f };
float* stereoBuffer[2] = { bufferL, bufferR };
HyperBufferViewNC<float, 2> wrapper(stereoBuffer, 2, 3);
wrapper[0][1] = 0.22f; // access left channel, second sample

Requirements / Compatibility

  • C++14, STL only
  • Compiled & Tested with:
    • Linux / macos / Windwos
    • GCC, Clang and MSVC
    • x86_64 and arm64 architectures

Design paradigms:

HyperBuffer is designed as a multi-dimensional counterpart to std::array and/or std::vector. However, it differs from said standard library classes on the 'points of commitment', i.e. the point in time at which certain parameters have to be specified (and cannot be changed afterwards):

HyperBufferstd::arraystd::vector
element data type (T)compile-timecompile-timecompile-time
number of dimensions (N)compile-time= 1= 1
extent of dimensionsconstruction-timecompile-timerun-time

HyperBuffer is thus a non-resizable container like std::array, however in contrast, the extent of the dimensions can be specified at runtime.

Note: For the time being, dimensions are constrained to be uniform, i.e. each 'slice' in a given dimension has equal length and data type.

Design choices were carefully weighed with the following prime directive in mind: avoid dynamic memory allocation as much as possible. This is crucial in realtime environments with a strict need for deterministic behaviour (e.g. audio processing threads).

Thanks to the chosen memory model, dynamic memory allocation happens only during construction. Furthermore, the entire data and pointer memory is each allocated in a single call (cf. documentation in Design Details), thereby avoiding memory fragmentation / churn.

Data Storage & Ownership Variants

HyperBuffer comes in 3 incarnations that use different levels of ownership on the data. In multi-dimensional structures, we can differentiate between the memory required to store the pointers.

ownershipuse case
HyperBufferowns/allocates pointers & dataStoring multi-dimensional data and providing a simple and safe API to it.
HyperBufferViewowns pointers, externally-allocated dataView for existing data in the HyperBuffer memory format (contiguous 1D memory) - e.g. a view to a sub-dimension of HyperBuffer
HyperBufferViewNCexternally-allocated pointers & dataWrapper for existing multi-dimensional data (non-contiguous memory, e.g. float**); gives it the same API as HyperBuffer

Note: Behaviour on copy & move: HyperBuffer copies/moves the data like a normal object with data ownership. When copying HyperBufferViewNC and HyperBufferView, however, the data is not duplicated - the copy references the original data as well.

API features & Memory Management:

In addition to information about the geometry (dimensions and extent thereof), the API has several ways of accessing data:

functiondescriptionreturn valueHyperBufferHyperBufferViewHyperBufferViewNC
.data()access the start of highest dimension of the dataraw pointer (e.g. float***)non-allocatingnon-allocatingnon-allocating
operator[.]access the N-1 sub-dimension at the given index; can be chained: h[3][0][6]raw pointer (e.g. float**); data value if N==1non-allocatingnon-allocatingnon-allocating
at(...)access data in lowest dimension (N arguments)data value (e.g. float)allocatingallocatingnon-allocating
subView(...)access data in any dimension (variable-length argument)N-x view to the dataallocatingallocatingnon-allocating

While HyperBufferViewNC never allocates memory under any circumstances, you can see above that the .at() and subView()accessors allocate dynamic memory for the other variants. This is because a new N-1 HyperBufferView is constructed, which allocates memory for the pointers.

Further guarantees:

  • accessing data is always allocation-free
  • dynamic allocation-free move() semantics
  • (planned) alignment of the data (lowest-order/innermost dimension) can be specified ('owning' mode only)

Build Status / Quality Metrics

Build & Test HyperBufferBuild & Test HyperBuffer

Quality Gate Status

BugsCode SmellsCoverageDuplicated Lines (%)

About

A C++ structure to manage multi-dimensional data efficiently and safely

Topics

Resources

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - Sidelobe/HyperBuffer: A C++ structure to manage multi-dimensional data efficiently and safely · GitHub
Skip to content

Repository files navigation

 ╦ ╦┬ ┬┌─┐┌─┐┬─┐ ╔╗ ┬ ┬┌─┐┌─┐┌─┐┬─┐
╠═╣└┬┘├─┘├┤ ├┬┘ ╠╩╗│ │├┤ ├┤ ├┤ ├┬┘
╩ ╩ ┴ ┴ └─┘┴└─ ╚═╝└─┘└ └ └─┘┴└─

A C++ structure to manage multi-dimensional data efficiently and safely

This container was designed to hold dynamically-allocated N-dimensional datasets in memory and provide convenient access to it, while minimizing performance/memory overhead as well as using a single allocation for all the data.

Usage Example

HyperBuffer<float, 2> buffer2D (2, 5);
float element01 = buffer2D[0][1];
float element14 = buffer2D.at(1, 4); // alternative to []float** rawPointer = buffer2D.data();
float* outerDimension0 = buffer2D[0];
HyperBufferView<float, 1> subView = buffer2D.subView(1);
float* outerDimension1 = subView.data();
// Any number of dimensions
HyperBuffer<int, 8> buffer8D (3, 4, 3, 1, 6, 256, 11, 7); // Wrapper for existing multi-dimensional data (zero dynamic memory allocation!)float bufferL[]{ 0.1f, 0.2f, 0.3f }; float bufferR[]{ -0.1f, -0.2f, -0.3f };
float* stereoBuffer[2] = { bufferL, bufferR };
HyperBufferViewNC<float, 2> wrapper(stereoBuffer, 2, 3);
wrapper[0][1] = 0.22f; // access left channel, second sample

Requirements / Compatibility

  • C++14, STL only
  • Compiled & Tested with:
    • Linux / macos / Windwos
    • GCC, Clang and MSVC
    • x86_64 and arm64 architectures

Design paradigms:

HyperBuffer is designed as a multi-dimensional counterpart to std::array and/or std::vector. However, it differs from said standard library classes on the 'points of commitment', i.e. the point in time at which certain parameters have to be specified (and cannot be changed afterwards):

HyperBufferstd::arraystd::vector
element data type (T)compile-timecompile-timecompile-time
number of dimensions (N)compile-time= 1= 1
extent of dimensionsconstruction-timecompile-timerun-time

HyperBuffer is thus a non-resizable container like std::array, however in contrast, the extent of the dimensions can be specified at runtime.

Note: For the time being, dimensions are constrained to be uniform, i.e. each 'slice' in a given dimension has equal length and data type.

Design choices were carefully weighed with the following prime directive in mind: avoid dynamic memory allocation as much as possible. This is crucial in realtime environments with a strict need for deterministic behaviour (e.g. audio processing threads).

Thanks to the chosen memory model, dynamic memory allocation happens only during construction. Furthermore, the entire data and pointer memory is each allocated in a single call (cf. documentation in Design Details), thereby avoiding memory fragmentation / churn.

Data Storage & Ownership Variants

HyperBuffer comes in 3 incarnations that use different levels of ownership on the data. In multi-dimensional structures, we can differentiate between the memory required to store the pointers.

ownershipuse case
HyperBufferowns/allocates pointers & dataStoring multi-dimensional data and providing a simple and safe API to it.
HyperBufferViewowns pointers, externally-allocated dataView for existing data in the HyperBuffer memory format (contiguous 1D memory) - e.g. a view to a sub-dimension of HyperBuffer
HyperBufferViewNCexternally-allocated pointers & dataWrapper for existing multi-dimensional data (non-contiguous memory, e.g. float**); gives it the same API as HyperBuffer

Note: Behaviour on copy & move: HyperBuffer copies/moves the data like a normal object with data ownership. When copying HyperBufferViewNC and HyperBufferView, however, the data is not duplicated - the copy references the original data as well.

API features & Memory Management:

In addition to information about the geometry (dimensions and extent thereof), the API has several ways of accessing data:

functiondescriptionreturn valueHyperBufferHyperBufferViewHyperBufferViewNC
.data()access the start of highest dimension of the dataraw pointer (e.g. float***)non-allocatingnon-allocatingnon-allocating
operator[.]access the N-1 sub-dimension at the given index; can be chained: h[3][0][6]raw pointer (e.g. float**); data value if N==1non-allocatingnon-allocatingnon-allocating
at(...)access data in lowest dimension (N arguments)data value (e.g. float)allocatingallocatingnon-allocating
subView(...)access data in any dimension (variable-length argument)N-x view to the dataallocatingallocatingnon-allocating

While HyperBufferViewNC never allocates memory under any circumstances, you can see above that the .at() and subView()accessors allocate dynamic memory for the other variants. This is because a new N-1 HyperBufferView is constructed, which allocates memory for the pointers.

Further guarantees:

  • accessing data is always allocation-free
  • dynamic allocation-free move() semantics
  • (planned) alignment of the data (lowest-order/innermost dimension) can be specified ('owning' mode only)

Build Status / Quality Metrics

Build & Test HyperBufferBuild & Test HyperBuffer

Quality Gate Status

BugsCode SmellsCoverageDuplicated Lines (%)

About

A C++ structure to manage multi-dimensional data efficiently and safely

Topics

Resources

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - Sidelobe/HyperBuffer: A C++ structure to manage multi-dimensional data efficiently and safely · GitHub
Skip to content

Repository files navigation

 ╦ ╦┬ ┬┌─┐┌─┐┬─┐ ╔╗ ┬ ┬┌─┐┌─┐┌─┐┬─┐
╠═╣└┬┘├─┘├┤ ├┬┘ ╠╩╗│ │├┤ ├┤ ├┤ ├┬┘
╩ ╩ ┴ ┴ └─┘┴└─ ╚═╝└─┘└ └ └─┘┴└─

A C++ structure to manage multi-dimensional data efficiently and safely

This container was designed to hold dynamically-allocated N-dimensional datasets in memory and provide convenient access to it, while minimizing performance/memory overhead as well as using a single allocation for all the data.

Usage Example

HyperBuffer<float, 2> buffer2D (2, 5);
float element01 = buffer2D[0][1];
float element14 = buffer2D.at(1, 4); // alternative to []float** rawPointer = buffer2D.data();
float* outerDimension0 = buffer2D[0];
HyperBufferView<float, 1> subView = buffer2D.subView(1);
float* outerDimension1 = subView.data();
// Any number of dimensions
HyperBuffer<int, 8> buffer8D (3, 4, 3, 1, 6, 256, 11, 7); // Wrapper for existing multi-dimensional data (zero dynamic memory allocation!)float bufferL[]{ 0.1f, 0.2f, 0.3f }; float bufferR[]{ -0.1f, -0.2f, -0.3f };
float* stereoBuffer[2] = { bufferL, bufferR };
HyperBufferViewNC<float, 2> wrapper(stereoBuffer, 2, 3);
wrapper[0][1] = 0.22f; // access left channel, second sample

Requirements / Compatibility

  • C++14, STL only
  • Compiled & Tested with:
    • Linux / macos / Windwos
    • GCC, Clang and MSVC
    • x86_64 and arm64 architectures

Design paradigms:

HyperBuffer is designed as a multi-dimensional counterpart to std::array and/or std::vector. However, it differs from said standard library classes on the 'points of commitment', i.e. the point in time at which certain parameters have to be specified (and cannot be changed afterwards):

HyperBufferstd::arraystd::vector
element data type (T)compile-timecompile-timecompile-time
number of dimensions (N)compile-time= 1= 1
extent of dimensionsconstruction-timecompile-timerun-time

HyperBuffer is thus a non-resizable container like std::array, however in contrast, the extent of the dimensions can be specified at runtime.

Note: For the time being, dimensions are constrained to be uniform, i.e. each 'slice' in a given dimension has equal length and data type.

Design choices were carefully weighed with the following prime directive in mind: avoid dynamic memory allocation as much as possible. This is crucial in realtime environments with a strict need for deterministic behaviour (e.g. audio processing threads).

Thanks to the chosen memory model, dynamic memory allocation happens only during construction. Furthermore, the entire data and pointer memory is each allocated in a single call (cf. documentation in Design Details), thereby avoiding memory fragmentation / churn.

Data Storage & Ownership Variants

HyperBuffer comes in 3 incarnations that use different levels of ownership on the data. In multi-dimensional structures, we can differentiate between the memory required to store the pointers.

ownershipuse case
HyperBufferowns/allocates pointers & dataStoring multi-dimensional data and providing a simple and safe API to it.
HyperBufferViewowns pointers, externally-allocated dataView for existing data in the HyperBuffer memory format (contiguous 1D memory) - e.g. a view to a sub-dimension of HyperBuffer
HyperBufferViewNCexternally-allocated pointers & dataWrapper for existing multi-dimensional data (non-contiguous memory, e.g. float**); gives it the same API as HyperBuffer

Note: Behaviour on copy & move: HyperBuffer copies/moves the data like a normal object with data ownership. When copying HyperBufferViewNC and HyperBufferView, however, the data is not duplicated - the copy references the original data as well.

API features & Memory Management:

In addition to information about the geometry (dimensions and extent thereof), the API has several ways of accessing data:

functiondescriptionreturn valueHyperBufferHyperBufferViewHyperBufferViewNC
.data()access the start of highest dimension of the dataraw pointer (e.g. float***)non-allocatingnon-allocatingnon-allocating
operator[.]access the N-1 sub-dimension at the given index; can be chained: h[3][0][6]raw pointer (e.g. float**); data value if N==1non-allocatingnon-allocatingnon-allocating
at(...)access data in lowest dimension (N arguments)data value (e.g. float)allocatingallocatingnon-allocating
subView(...)access data in any dimension (variable-length argument)N-x view to the dataallocatingallocatingnon-allocating

While HyperBufferViewNC never allocates memory under any circumstances, you can see above that the .at() and subView()accessors allocate dynamic memory for the other variants. This is because a new N-1 HyperBufferView is constructed, which allocates memory for the pointers.

Further guarantees:

  • accessing data is always allocation-free
  • dynamic allocation-free move() semantics
  • (planned) alignment of the data (lowest-order/innermost dimension) can be specified ('owning' mode only)

Build Status / Quality Metrics

Build & Test HyperBufferBuild & Test HyperBuffer

Quality Gate Status

BugsCode SmellsCoverageDuplicated Lines (%)

About

A C++ structure to manage multi-dimensional data efficiently and safely

Topics

Resources

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - Sidelobe/HyperBuffer: A C++ structure to manage multi-dimensional data efficiently and safely · GitHub
Skip to content

Repository files navigation

 ╦ ╦┬ ┬┌─┐┌─┐┬─┐ ╔╗ ┬ ┬┌─┐┌─┐┌─┐┬─┐
╠═╣└┬┘├─┘├┤ ├┬┘ ╠╩╗│ │├┤ ├┤ ├┤ ├┬┘
╩ ╩ ┴ ┴ └─┘┴└─ ╚═╝└─┘└ └ └─┘┴└─

A C++ structure to manage multi-dimensional data efficiently and safely

This container was designed to hold dynamically-allocated N-dimensional datasets in memory and provide convenient access to it, while minimizing performance/memory overhead as well as using a single allocation for all the data.

Usage Example

HyperBuffer<float, 2> buffer2D (2, 5);
float element01 = buffer2D[0][1];
float element14 = buffer2D.at(1, 4); // alternative to []float** rawPointer = buffer2D.data();
float* outerDimension0 = buffer2D[0];
HyperBufferView<float, 1> subView = buffer2D.subView(1);
float* outerDimension1 = subView.data();
// Any number of dimensions
HyperBuffer<int, 8> buffer8D (3, 4, 3, 1, 6, 256, 11, 7); // Wrapper for existing multi-dimensional data (zero dynamic memory allocation!)float bufferL[]{ 0.1f, 0.2f, 0.3f }; float bufferR[]{ -0.1f, -0.2f, -0.3f };
float* stereoBuffer[2] = { bufferL, bufferR };
HyperBufferViewNC<float, 2> wrapper(stereoBuffer, 2, 3);
wrapper[0][1] = 0.22f; // access left channel, second sample

Requirements / Compatibility

  • C++14, STL only
  • Compiled & Tested with:
    • Linux / macos / Windwos
    • GCC, Clang and MSVC
    • x86_64 and arm64 architectures

Design paradigms:

HyperBuffer is designed as a multi-dimensional counterpart to std::array and/or std::vector. However, it differs from said standard library classes on the 'points of commitment', i.e. the point in time at which certain parameters have to be specified (and cannot be changed afterwards):

HyperBufferstd::arraystd::vector
element data type (T)compile-timecompile-timecompile-time
number of dimensions (N)compile-time= 1= 1
extent of dimensionsconstruction-timecompile-timerun-time

HyperBuffer is thus a non-resizable container like std::array, however in contrast, the extent of the dimensions can be specified at runtime.

Note: For the time being, dimensions are constrained to be uniform, i.e. each 'slice' in a given dimension has equal length and data type.

Design choices were carefully weighed with the following prime directive in mind: avoid dynamic memory allocation as much as possible. This is crucial in realtime environments with a strict need for deterministic behaviour (e.g. audio processing threads).

Thanks to the chosen memory model, dynamic memory allocation happens only during construction. Furthermore, the entire data and pointer memory is each allocated in a single call (cf. documentation in Design Details), thereby avoiding memory fragmentation / churn.

Data Storage & Ownership Variants

HyperBuffer comes in 3 incarnations that use different levels of ownership on the data. In multi-dimensional structures, we can differentiate between the memory required to store the pointers.

ownershipuse case
HyperBufferowns/allocates pointers & dataStoring multi-dimensional data and providing a simple and safe API to it.
HyperBufferViewowns pointers, externally-allocated dataView for existing data in the HyperBuffer memory format (contiguous 1D memory) - e.g. a view to a sub-dimension of HyperBuffer
HyperBufferViewNCexternally-allocated pointers & dataWrapper for existing multi-dimensional data (non-contiguous memory, e.g. float**); gives it the same API as HyperBuffer

Note: Behaviour on copy & move: HyperBuffer copies/moves the data like a normal object with data ownership. When copying HyperBufferViewNC and HyperBufferView, however, the data is not duplicated - the copy references the original data as well.

API features & Memory Management:

In addition to information about the geometry (dimensions and extent thereof), the API has several ways of accessing data:

functiondescriptionreturn valueHyperBufferHyperBufferViewHyperBufferViewNC
.data()access the start of highest dimension of the dataraw pointer (e.g. float***)non-allocatingnon-allocatingnon-allocating
operator[.]access the N-1 sub-dimension at the given index; can be chained: h[3][0][6]raw pointer (e.g. float**); data value if N==1non-allocatingnon-allocatingnon-allocating
at(...)access data in lowest dimension (N arguments)data value (e.g. float)allocatingallocatingnon-allocating
subView(...)access data in any dimension (variable-length argument)N-x view to the dataallocatingallocatingnon-allocating

While HyperBufferViewNC never allocates memory under any circumstances, you can see above that the .at() and subView()accessors allocate dynamic memory for the other variants. This is because a new N-1 HyperBufferView is constructed, which allocates memory for the pointers.

Further guarantees:

  • accessing data is always allocation-free
  • dynamic allocation-free move() semantics
  • (planned) alignment of the data (lowest-order/innermost dimension) can be specified ('owning' mode only)

Build Status / Quality Metrics

Build & Test HyperBufferBuild & Test HyperBuffer

Quality Gate Status

BugsCode SmellsCoverageDuplicated Lines (%)

About

A C++ structure to manage multi-dimensional data efficiently and safely

Topics

Resources

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - Sidelobe/HyperBuffer: A C++ structure to manage multi-dimensional data efficiently and safely · GitHub
Skip to content

Repository files navigation

 ╦ ╦┬ ┬┌─┐┌─┐┬─┐ ╔╗ ┬ ┬┌─┐┌─┐┌─┐┬─┐
╠═╣└┬┘├─┘├┤ ├┬┘ ╠╩╗│ │├┤ ├┤ ├┤ ├┬┘
╩ ╩ ┴ ┴ └─┘┴└─ ╚═╝└─┘└ └ └─┘┴└─

A C++ structure to manage multi-dimensional data efficiently and safely

This container was designed to hold dynamically-allocated N-dimensional datasets in memory and provide convenient access to it, while minimizing performance/memory overhead as well as using a single allocation for all the data.

Usage Example

HyperBuffer<float, 2> buffer2D (2, 5);
float element01 = buffer2D[0][1];
float element14 = buffer2D.at(1, 4); // alternative to []float** rawPointer = buffer2D.data();
float* outerDimension0 = buffer2D[0];
HyperBufferView<float, 1> subView = buffer2D.subView(1);
float* outerDimension1 = subView.data();
// Any number of dimensions
HyperBuffer<int, 8> buffer8D (3, 4, 3, 1, 6, 256, 11, 7); // Wrapper for existing multi-dimensional data (zero dynamic memory allocation!)float bufferL[]{ 0.1f, 0.2f, 0.3f }; float bufferR[]{ -0.1f, -0.2f, -0.3f };
float* stereoBuffer[2] = { bufferL, bufferR };
HyperBufferViewNC<float, 2> wrapper(stereoBuffer, 2, 3);
wrapper[0][1] = 0.22f; // access left channel, second sample

Requirements / Compatibility

  • C++14, STL only
  • Compiled & Tested with:
    • Linux / macos / Windwos
    • GCC, Clang and MSVC
    • x86_64 and arm64 architectures

Design paradigms:

HyperBuffer is designed as a multi-dimensional counterpart to std::array and/or std::vector. However, it differs from said standard library classes on the 'points of commitment', i.e. the point in time at which certain parameters have to be specified (and cannot be changed afterwards):

HyperBufferstd::arraystd::vector
element data type (T)compile-timecompile-timecompile-time
number of dimensions (N)compile-time= 1= 1
extent of dimensionsconstruction-timecompile-timerun-time

HyperBuffer is thus a non-resizable container like std::array, however in contrast, the extent of the dimensions can be specified at runtime.

Note: For the time being, dimensions are constrained to be uniform, i.e. each 'slice' in a given dimension has equal length and data type.

Design choices were carefully weighed with the following prime directive in mind: avoid dynamic memory allocation as much as possible. This is crucial in realtime environments with a strict need for deterministic behaviour (e.g. audio processing threads).

Thanks to the chosen memory model, dynamic memory allocation happens only during construction. Furthermore, the entire data and pointer memory is each allocated in a single call (cf. documentation in Design Details), thereby avoiding memory fragmentation / churn.

Data Storage & Ownership Variants

HyperBuffer comes in 3 incarnations that use different levels of ownership on the data. In multi-dimensional structures, we can differentiate between the memory required to store the pointers.

ownershipuse case
HyperBufferowns/allocates pointers & dataStoring multi-dimensional data and providing a simple and safe API to it.
HyperBufferViewowns pointers, externally-allocated dataView for existing data in the HyperBuffer memory format (contiguous 1D memory) - e.g. a view to a sub-dimension of HyperBuffer
HyperBufferViewNCexternally-allocated pointers & dataWrapper for existing multi-dimensional data (non-contiguous memory, e.g. float**); gives it the same API as HyperBuffer

Note: Behaviour on copy & move: HyperBuffer copies/moves the data like a normal object with data ownership. When copying HyperBufferViewNC and HyperBufferView, however, the data is not duplicated - the copy references the original data as well.

API features & Memory Management:

In addition to information about the geometry (dimensions and extent thereof), the API has several ways of accessing data:

functiondescriptionreturn valueHyperBufferHyperBufferViewHyperBufferViewNC
.data()access the start of highest dimension of the dataraw pointer (e.g. float***)non-allocatingnon-allocatingnon-allocating
operator[.]access the N-1 sub-dimension at the given index; can be chained: h[3][0][6]raw pointer (e.g. float**); data value if N==1non-allocatingnon-allocatingnon-allocating
at(...)access data in lowest dimension (N arguments)data value (e.g. float)allocatingallocatingnon-allocating
subView(...)access data in any dimension (variable-length argument)N-x view to the dataallocatingallocatingnon-allocating

While HyperBufferViewNC never allocates memory under any circumstances, you can see above that the .at() and subView()accessors allocate dynamic memory for the other variants. This is because a new N-1 HyperBufferView is constructed, which allocates memory for the pointers.

Further guarantees:

  • accessing data is always allocation-free
  • dynamic allocation-free move() semantics
  • (planned) alignment of the data (lowest-order/innermost dimension) can be specified ('owning' mode only)

Build Status / Quality Metrics

Build & Test HyperBufferBuild & Test HyperBuffer

Quality Gate Status

BugsCode SmellsCoverageDuplicated Lines (%)

About

A C++ structure to manage multi-dimensional data efficiently and safely

Topics

Resources

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - Sidelobe/HyperBuffer: A C++ structure to manage multi-dimensional data efficiently and safely · GitHub
Skip to content

Repository files navigation

 ╦ ╦┬ ┬┌─┐┌─┐┬─┐ ╔╗ ┬ ┬┌─┐┌─┐┌─┐┬─┐
╠═╣└┬┘├─┘├┤ ├┬┘ ╠╩╗│ │├┤ ├┤ ├┤ ├┬┘
╩ ╩ ┴ ┴ └─┘┴└─ ╚═╝└─┘└ └ └─┘┴└─

A C++ structure to manage multi-dimensional data efficiently and safely

This container was designed to hold dynamically-allocated N-dimensional datasets in memory and provide convenient access to it, while minimizing performance/memory overhead as well as using a single allocation for all the data.

Usage Example

HyperBuffer<float, 2> buffer2D (2, 5);
float element01 = buffer2D[0][1];
float element14 = buffer2D.at(1, 4); // alternative to []float** rawPointer = buffer2D.data();
float* outerDimension0 = buffer2D[0];
HyperBufferView<float, 1> subView = buffer2D.subView(1);
float* outerDimension1 = subView.data();
// Any number of dimensions
HyperBuffer<int, 8> buffer8D (3, 4, 3, 1, 6, 256, 11, 7); // Wrapper for existing multi-dimensional data (zero dynamic memory allocation!)float bufferL[]{ 0.1f, 0.2f, 0.3f }; float bufferR[]{ -0.1f, -0.2f, -0.3f };
float* stereoBuffer[2] = { bufferL, bufferR };
HyperBufferViewNC<float, 2> wrapper(stereoBuffer, 2, 3);
wrapper[0][1] = 0.22f; // access left channel, second sample

Requirements / Compatibility

  • C++14, STL only
  • Compiled & Tested with:
    • Linux / macos / Windwos
    • GCC, Clang and MSVC
    • x86_64 and arm64 architectures

Design paradigms:

HyperBuffer is designed as a multi-dimensional counterpart to std::array and/or std::vector. However, it differs from said standard library classes on the 'points of commitment', i.e. the point in time at which certain parameters have to be specified (and cannot be changed afterwards):

HyperBufferstd::arraystd::vector
element data type (T)compile-timecompile-timecompile-time
number of dimensions (N)compile-time= 1= 1
extent of dimensionsconstruction-timecompile-timerun-time

HyperBuffer is thus a non-resizable container like std::array, however in contrast, the extent of the dimensions can be specified at runtime.

Note: For the time being, dimensions are constrained to be uniform, i.e. each 'slice' in a given dimension has equal length and data type.

Design choices were carefully weighed with the following prime directive in mind: avoid dynamic memory allocation as much as possible. This is crucial in realtime environments with a strict need for deterministic behaviour (e.g. audio processing threads).

Thanks to the chosen memory model, dynamic memory allocation happens only during construction. Furthermore, the entire data and pointer memory is each allocated in a single call (cf. documentation in Design Details), thereby avoiding memory fragmentation / churn.

Data Storage & Ownership Variants

HyperBuffer comes in 3 incarnations that use different levels of ownership on the data. In multi-dimensional structures, we can differentiate between the memory required to store the pointers.

ownershipuse case
HyperBufferowns/allocates pointers & dataStoring multi-dimensional data and providing a simple and safe API to it.
HyperBufferViewowns pointers, externally-allocated dataView for existing data in the HyperBuffer memory format (contiguous 1D memory) - e.g. a view to a sub-dimension of HyperBuffer
HyperBufferViewNCexternally-allocated pointers & dataWrapper for existing multi-dimensional data (non-contiguous memory, e.g. float**); gives it the same API as HyperBuffer

Note: Behaviour on copy & move: HyperBuffer copies/moves the data like a normal object with data ownership. When copying HyperBufferViewNC and HyperBufferView, however, the data is not duplicated - the copy references the original data as well.

API features & Memory Management:

In addition to information about the geometry (dimensions and extent thereof), the API has several ways of accessing data:

functiondescriptionreturn valueHyperBufferHyperBufferViewHyperBufferViewNC
.data()access the start of highest dimension of the dataraw pointer (e.g. float***)non-allocatingnon-allocatingnon-allocating
operator[.]access the N-1 sub-dimension at the given index; can be chained: h[3][0][6]raw pointer (e.g. float**); data value if N==1non-allocatingnon-allocatingnon-allocating
at(...)access data in lowest dimension (N arguments)data value (e.g. float)allocatingallocatingnon-allocating
subView(...)access data in any dimension (variable-length argument)N-x view to the dataallocatingallocatingnon-allocating

While HyperBufferViewNC never allocates memory under any circumstances, you can see above that the .at() and subView()accessors allocate dynamic memory for the other variants. This is because a new N-1 HyperBufferView is constructed, which allocates memory for the pointers.

Further guarantees:

  • accessing data is always allocation-free
  • dynamic allocation-free move() semantics
  • (planned) alignment of the data (lowest-order/innermost dimension) can be specified ('owning' mode only)

Build Status / Quality Metrics

Build & Test HyperBufferBuild & Test HyperBuffer

Quality Gate Status

BugsCode SmellsCoverageDuplicated Lines (%)

About

A C++ structure to manage multi-dimensional data efficiently and safely

Topics

Resources

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - Sidelobe/HyperBuffer: A C++ structure to manage multi-dimensional data efficiently and safely · GitHub
Skip to content

Repository files navigation

 ╦ ╦┬ ┬┌─┐┌─┐┬─┐ ╔╗ ┬ ┬┌─┐┌─┐┌─┐┬─┐
╠═╣└┬┘├─┘├┤ ├┬┘ ╠╩╗│ │├┤ ├┤ ├┤ ├┬┘
╩ ╩ ┴ ┴ └─┘┴└─ ╚═╝└─┘└ └ └─┘┴└─

A C++ structure to manage multi-dimensional data efficiently and safely

This container was designed to hold dynamically-allocated N-dimensional datasets in memory and provide convenient access to it, while minimizing performance/memory overhead as well as using a single allocation for all the data.

Usage Example

HyperBuffer<float, 2> buffer2D (2, 5);
float element01 = buffer2D[0][1];
float element14 = buffer2D.at(1, 4); // alternative to []float** rawPointer = buffer2D.data();
float* outerDimension0 = buffer2D[0];
HyperBufferView<float, 1> subView = buffer2D.subView(1);
float* outerDimension1 = subView.data();
// Any number of dimensions
HyperBuffer<int, 8> buffer8D (3, 4, 3, 1, 6, 256, 11, 7); // Wrapper for existing multi-dimensional data (zero dynamic memory allocation!)float bufferL[]{ 0.1f, 0.2f, 0.3f }; float bufferR[]{ -0.1f, -0.2f, -0.3f };
float* stereoBuffer[2] = { bufferL, bufferR };
HyperBufferViewNC<float, 2> wrapper(stereoBuffer, 2, 3);
wrapper[0][1] = 0.22f; // access left channel, second sample

Requirements / Compatibility

  • C++14, STL only
  • Compiled & Tested with:
    • Linux / macos / Windwos
    • GCC, Clang and MSVC
    • x86_64 and arm64 architectures

Design paradigms:

HyperBuffer is designed as a multi-dimensional counterpart to std::array and/or std::vector. However, it differs from said standard library classes on the 'points of commitment', i.e. the point in time at which certain parameters have to be specified (and cannot be changed afterwards):

HyperBufferstd::arraystd::vector
element data type (T)compile-timecompile-timecompile-time
number of dimensions (N)compile-time= 1= 1
extent of dimensionsconstruction-timecompile-timerun-time

HyperBuffer is thus a non-resizable container like std::array, however in contrast, the extent of the dimensions can be specified at runtime.

Note: For the time being, dimensions are constrained to be uniform, i.e. each 'slice' in a given dimension has equal length and data type.

Design choices were carefully weighed with the following prime directive in mind: avoid dynamic memory allocation as much as possible. This is crucial in realtime environments with a strict need for deterministic behaviour (e.g. audio processing threads).

Thanks to the chosen memory model, dynamic memory allocation happens only during construction. Furthermore, the entire data and pointer memory is each allocated in a single call (cf. documentation in Design Details), thereby avoiding memory fragmentation / churn.

Data Storage & Ownership Variants

HyperBuffer comes in 3 incarnations that use different levels of ownership on the data. In multi-dimensional structures, we can differentiate between the memory required to store the pointers.

ownershipuse case
HyperBufferowns/allocates pointers & dataStoring multi-dimensional data and providing a simple and safe API to it.
HyperBufferViewowns pointers, externally-allocated dataView for existing data in the HyperBuffer memory format (contiguous 1D memory) - e.g. a view to a sub-dimension of HyperBuffer
HyperBufferViewNCexternally-allocated pointers & dataWrapper for existing multi-dimensional data (non-contiguous memory, e.g. float**); gives it the same API as HyperBuffer

Note: Behaviour on copy & move: HyperBuffer copies/moves the data like a normal object with data ownership. When copying HyperBufferViewNC and HyperBufferView, however, the data is not duplicated - the copy references the original data as well.

API features & Memory Management:

In addition to information about the geometry (dimensions and extent thereof), the API has several ways of accessing data:

functiondescriptionreturn valueHyperBufferHyperBufferViewHyperBufferViewNC
.data()access the start of highest dimension of the dataraw pointer (e.g. float***)non-allocatingnon-allocatingnon-allocating
operator[.]access the N-1 sub-dimension at the given index; can be chained: h[3][0][6]raw pointer (e.g. float**); data value if N==1non-allocatingnon-allocatingnon-allocating
at(...)access data in lowest dimension (N arguments)data value (e.g. float)allocatingallocatingnon-allocating
subView(...)access data in any dimension (variable-length argument)N-x view to the dataallocatingallocatingnon-allocating

While HyperBufferViewNC never allocates memory under any circumstances, you can see above that the .at() and subView()accessors allocate dynamic memory for the other variants. This is because a new N-1 HyperBufferView is constructed, which allocates memory for the pointers.

Further guarantees:

  • accessing data is always allocation-free
  • dynamic allocation-free move() semantics
  • (planned) alignment of the data (lowest-order/innermost dimension) can be specified ('owning' mode only)

Build Status / Quality Metrics

Build & Test HyperBufferBuild & Test HyperBuffer

Quality Gate Status

BugsCode SmellsCoverageDuplicated Lines (%)

About

A C++ structure to manage multi-dimensional data efficiently and safely

Topics

Resources

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages