/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 7 End-User License Agreement and JUCE Privacy Policy. End User License Agreement: www.juce.com/juce-7-licence Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce::midi_ci::detail { PropertyDataMessageChunker::PropertyDataMessageChunker (std::vector& storageIn, int chunkSizeIn, const std::byte messageKindIn, const std::byte requestIdIn, Span headerIn, MUID sourceIn, MUID destIn, InputStream& bodyIn) : header (headerIn), storage (&storageIn), body (&bodyIn), source (sourceIn), dest (destIn), chunkSize (chunkSizeIn), messageKind (messageKindIn), requestId (requestIdIn) { if (hasRoomForBody()) { populateStorage(); } else { // Header too large! There's no way to fit this message into the requested chunk size. jassertfalse; *this = PropertyDataMessageChunker(); } } PropertyDataMessageChunker& PropertyDataMessageChunker::operator++() noexcept { if (*this != PropertyDataMessageChunker()) { if (body->isExhausted()) { *this = PropertyDataMessageChunker(); } else { ++thisChunk; populateStorage(); } } return *this; } Span PropertyDataMessageChunker::operator*() const noexcept { // The end of the stream was reached, no point dereferencing the iterator now! jassert (storage != nullptr && (int) storage->size() <= chunkSize); return *storage; } Span PropertyDataMessageChunker::getHeaderForBlock() const { return thisChunk == 1 ? header : Span{}; } int PropertyDataMessageChunker::getRoomForBody() const { return chunkSize - (int) (getHeaderForBlock().size() + 22); } bool PropertyDataMessageChunker::hasRoomForBody() const { const auto bodyRoom = getRoomForBody(); return (0 < bodyRoom) || (0 == bodyRoom && body->getNumBytesRemaining() == 0); } void PropertyDataMessageChunker::populateStorage() const { storage->clear(); storage->resize ((size_t) getRoomForBody()); // Read body data into buffer const auto numBytesRead = (uint16_t) jmax (ssize_t (0), body->read (storage->data(), storage->size())); const auto [numChunks, thisChunkNum] = [&]() -> std::tuple { if (body->isExhausted() || body->getNumBytesRemaining() == 0) return std::tuple (thisChunk, thisChunk); const auto totalLength = body->getTotalLength(); if (totalLength < 0) return std::tuple ((uint16_t) 0, thisChunk); // 0 means "unknown number" const auto roomForBody = getRoomForBody(); if (roomForBody != 0) return std::tuple ((uint16_t) ((totalLength + roomForBody - 1) / roomForBody), thisChunk); // During construction, the input stream reported that it had no data remaining, so no // space was reserved for body content. // Now, the input stream reports that it has data remaining, but there's nowhere // to fit it in the message! jassertfalse; return std::tuple (thisChunk, (uint16_t) 0); // 0 means "data potentially unusable" }(); // Now we know how many bytes we managed to read, write the header at the end of the buffer const auto headerForBlock = getHeaderForBlock(); detail::Marshalling::Writer writer { *storage }; writer (Message::Header { ChannelInGroup::wholeBlock, messageKind, detail::MessageMeta::implementationVersion, source, dest }, requestId, detail::MessageMeta::makeSpanWithSizeBytes<2> (headerForBlock), numChunks, thisChunkNum, numBytesRead); // Finally, swap the header to the beginning of the buffer std::rotate (storage->begin(), storage->begin() + getRoomForBody(), storage->end()); // ...and bring the storage buffer down to size, if we didn't manage to fill it const auto room = (size_t) getRoomForBody(); storage->resize (storage->size() + numBytesRead - room); } } // namespace juce::midi_ci::detail