/* * Copyright (C) 2006-2021 Apple Inc. All rights reserved. * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. * Copyright (C) 2015 Canon Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "SharedBuffer.h" #include #include #include #include #include namespace WebCore { SharedBuffer::SharedBuffer(const uint8_t* data, size_t size) { append(data, size); } SharedBuffer::SharedBuffer(const char* data, size_t size) { append(reinterpret_cast(data), size); } SharedBuffer::SharedBuffer(FileSystem::MappedFileData&& fileData) : m_size(fileData.size()) { m_segments.append({0, DataSegment::create(WTFMove(fileData))}); } SharedBuffer::SharedBuffer(Vector&& data) { append(WTFMove(data)); } #if USE(GSTREAMER) Ref SharedBuffer::create(GstMappedOwnedBuffer& mappedBuffer) { return adoptRef(*new SharedBuffer(mappedBuffer)); } SharedBuffer::SharedBuffer(GstMappedOwnedBuffer& mappedBuffer) : m_size(mappedBuffer.size()) { m_segments.append({0, DataSegment::create(&mappedBuffer)}); } #endif RefPtr SharedBuffer::createWithContentsOfFile(const String& filePath, FileSystem::MappedFileMode mappedFileMode, MayUseFileMapping mayUseFileMapping) { if (mayUseFileMapping == MayUseFileMapping::Yes) { bool mappingSuccess; FileSystem::MappedFileData mappedFileData(filePath, mappedFileMode, mappingSuccess); if (mappingSuccess) return adoptRef(new SharedBuffer(WTFMove(mappedFileData))); } return SharedBuffer::createFromReadingFile(filePath); } Ref SharedBuffer::create(Vector&& vector) { return adoptRef(*new SharedBuffer(WTFMove(vector))); } static Vector combineSegmentsData(const SharedBuffer::DataSegmentVector& segments, size_t size) { Vector combinedData; combinedData.reserveInitialCapacity(size); for (auto& segment : segments) combinedData.append(segment.segment->data(), segment.segment->size()); ASSERT(combinedData.size() == size); return combinedData; } void SharedBuffer::combineIntoOneSegment() const { #if ASSERT_ENABLED // FIXME: We ought to be able to set this to true and have no assertions fire. // Remove all instances of appending after calling this, because they are all O(n^2) algorithms since r215686. // m_hasBeenCombinedIntoOneSegment = true; #endif if (m_segments.size() <= 1) return; auto combinedData = combineSegmentsData(m_segments, m_size); m_segments.clear(); m_segments.append({0, DataSegment::create(WTFMove(combinedData))}); ASSERT(m_segments.size() == 1); ASSERT(internallyConsistent()); } const uint8_t* SharedBuffer::data() const { if (m_segments.isEmpty()) return nullptr; combineIntoOneSegment(); ASSERT(internallyConsistent()); return m_segments[0].segment->data(); } Vector SharedBuffer::copyData() const { Vector data; data.reserveInitialCapacity(size()); forEachSegment([&data](auto& span) { data.uncheckedAppend(span); }); return data; } Vector SharedBuffer::takeData() { if (m_segments.isEmpty()) return { }; Vector combinedData; if (hasOneSegment() && WTF::holds_alternative>(m_segments[0].segment->m_immutableData)) combinedData = std::exchange(WTF::get>(m_segments[0].segment->m_immutableData), Vector()); else combinedData = combineSegmentsData(m_segments, m_size); clear(); return combinedData; } SharedBufferDataView SharedBuffer::getSomeData(size_t position) const { RELEASE_ASSERT(position < m_size); auto comparator = [](const size_t& position, const DataSegmentVectorEntry& entry) { return position < entry.beginPosition; }; const DataSegmentVectorEntry* element = std::upper_bound(m_segments.begin(), m_segments.end(), position, comparator); element--; // std::upper_bound gives a pointer to the element that is greater than position. We want the element just before that. return { element->segment.copyRef(), position - element->beginPosition }; } String SharedBuffer::toHexString() const { StringBuilder stringBuilder; forEachSegment([&](auto& segment) { for (unsigned i = 0; i < segment.size(); ++i) stringBuilder.append(pad('0', 2, hex(segment[i]))); }); return stringBuilder.toString(); } RefPtr SharedBuffer::tryCreateArrayBuffer() const { auto arrayBuffer = ArrayBuffer::tryCreateUninitialized(static_cast(size()), 1); if (!arrayBuffer) { WTFLogAlways("SharedBuffer::tryCreateArrayBuffer Unable to create buffer. Requested size was %zu\n", size()); return nullptr; } size_t position = 0; for (const auto& segment : m_segments) { memcpy(static_cast(arrayBuffer->data()) + position, segment.segment->data(), segment.segment->size()); position += segment.segment->size(); } ASSERT(position == m_size); ASSERT(internallyConsistent()); return arrayBuffer; } void SharedBuffer::append(const SharedBuffer& data) { ASSERT(!m_hasBeenCombinedIntoOneSegment); m_segments.reserveCapacity(m_segments.size() + data.m_segments.size()); for (const auto& element : data.m_segments) { m_segments.uncheckedAppend({m_size, element.segment.copyRef()}); m_size += element.segment->size(); } ASSERT(internallyConsistent()); } void SharedBuffer::append(const uint8_t* data, size_t length) { ASSERT(!m_hasBeenCombinedIntoOneSegment); Vector vector; vector.append(data, length); m_segments.append({m_size, DataSegment::create(WTFMove(vector))}); m_size += length; ASSERT(internallyConsistent()); } void SharedBuffer::append(Vector&& data) { ASSERT(!m_hasBeenCombinedIntoOneSegment); auto dataSize = data.size(); m_segments.append({m_size, DataSegment::create(WTFMove(data))}); m_size += dataSize; ASSERT(internallyConsistent()); } void SharedBuffer::clear() { m_size = 0; m_segments.clear(); ASSERT(internallyConsistent()); } Ref SharedBuffer::copy() const { Ref clone = adoptRef(*new SharedBuffer); clone->m_size = m_size; clone->m_segments.reserveInitialCapacity(m_segments.size()); for (const auto& element : m_segments) clone->m_segments.uncheckedAppend({element.beginPosition, element.segment.copyRef()}); ASSERT(clone->internallyConsistent()); ASSERT(internallyConsistent()); return clone; } void SharedBuffer::forEachSegment(const Function&)>& apply) const { for (auto& segment : m_segments) apply(Span { segment.segment->data(), segment.segment->size() }); } bool SharedBuffer::startsWith(const Span& prefix) const { if (prefix.empty()) return true; if (size() < prefix.size()) return false; const uint8_t* prefixPtr = prefix.data(); size_t remaining = prefix.size(); for (auto& segment : m_segments) { size_t amountToCompareThisTime = std::min(remaining, segment.segment->size()); if (memcmp(prefixPtr, segment.segment->data(), amountToCompareThisTime)) return false; remaining -= amountToCompareThisTime; if (!remaining) return true; prefixPtr += amountToCompareThisTime; } return false; } void SharedBuffer::copyTo(void* destination, size_t length) const { ASSERT(length <= size()); auto destinationPtr = static_cast(destination); auto remaining = std::min(length, size()); for (auto& segment : m_segments) { size_t amountToCopyThisTime = std::min(remaining, segment.segment->size()); memcpy(destinationPtr, segment.segment->data(), amountToCopyThisTime); remaining -= amountToCopyThisTime; if (!remaining) return; destinationPtr += amountToCopyThisTime; } } bool SharedBuffer::hasOneSegment() const { auto it = begin(); return it != end() && ++it == end(); } #if ASSERT_ENABLED bool SharedBuffer::internallyConsistent() const { size_t position = 0; for (const auto& element : m_segments) { if (element.beginPosition != position) return false; position += element.segment->size(); } return position == m_size; } #endif // ASSERT_ENABLED const uint8_t* SharedBuffer::DataSegment::data() const { auto visitor = WTF::makeVisitor( [](const Vector& data) { return data.data(); }, #if USE(CF) [](const RetainPtr& data) { return CFDataGetBytePtr(data.get()); }, #endif #if USE(GLIB) [](const GRefPtr& data) { return static_cast(g_bytes_get_data(data.get(), nullptr)); }, #endif #if USE(GSTREAMER) [](const RefPtr& data) { return data->data(); }, #endif [](const FileSystem::MappedFileData& data) { return static_cast(data.data()); } ); return WTF::visit(visitor, m_immutableData); } bool SharedBuffer::DataSegment::containsMappedFileData() const { return WTF::holds_alternative(m_immutableData); } #if !USE(CF) void SharedBuffer::hintMemoryNotNeededSoon() const { } #endif WTF::Persistence::Decoder SharedBuffer::decoder() const { return {{ reinterpret_cast(data()), size() }}; } bool SharedBuffer::operator==(const SharedBuffer& other) const { if (this == &other) return true; if (m_size != other.m_size) return false; auto thisIterator = begin(); size_t thisOffset = 0; auto otherIterator = other.begin(); size_t otherOffset = 0; while (thisIterator != end() && otherIterator != other.end()) { auto& thisSegment = thisIterator->segment.get(); auto& otherSegment = otherIterator->segment.get(); if (&thisSegment == &otherSegment && !thisOffset && !otherOffset) { ++thisIterator; ++otherIterator; continue; } ASSERT(thisOffset <= thisSegment.size()); ASSERT(otherOffset <= otherSegment.size()); size_t thisRemaining = thisSegment.size() - thisOffset; size_t otherRemaining = otherSegment.size() - otherOffset; size_t remaining = std::min(thisRemaining, otherRemaining); if (memcmp(thisSegment.data() + thisOffset, otherSegment.data() + otherOffset, remaining)) return false; thisOffset += remaining; otherOffset += remaining; if (thisOffset == thisSegment.size()) { ++thisIterator; thisOffset = 0; } if (otherOffset == otherSegment.size()) { ++otherIterator; otherOffset = 0; } } return true; } size_t SharedBuffer::DataSegment::size() const { auto visitor = WTF::makeVisitor( [](const Vector& data) { return data.size(); }, #if USE(CF) [](const RetainPtr& data) { return CFDataGetLength(data.get()); }, #endif #if USE(GLIB) [](const GRefPtr& data) { return g_bytes_get_size(data.get()); }, #endif #if USE(GSTREAMER) [](const RefPtr& data) { return data->size(); }, #endif [](const FileSystem::MappedFileData& data) { return data.size(); } ); return WTF::visit(visitor, m_immutableData); } SharedBufferDataView::SharedBufferDataView(Ref&& segment, size_t positionWithinSegment) : m_positionWithinSegment(positionWithinSegment) , m_segment(WTFMove(segment)) { RELEASE_ASSERT(positionWithinSegment < m_segment->size()); } size_t SharedBufferDataView::size() const { return m_segment->size() - m_positionWithinSegment; } const uint8_t* SharedBufferDataView::data() const { return m_segment->data() + m_positionWithinSegment; } RefPtr utf8Buffer(const String& string) { // Allocate a buffer big enough to hold all the characters. const size_t length = string.length(); if constexpr (String::MaxLength > std::numeric_limits::max() / 3) { if (length > std::numeric_limits::max() / 3) return nullptr; } Vector buffer(length * 3); // Convert to runs of 8-bit characters. char* p = reinterpret_cast(buffer.data()); if (length) { if (string.is8Bit()) { const LChar* d = string.characters8(); if (!WTF::Unicode::convertLatin1ToUTF8(&d, d + length, &p, p + buffer.size())) return nullptr; } else { const UChar* d = string.characters16(); if (WTF::Unicode::convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size()) != WTF::Unicode::ConversionOK) return nullptr; } } buffer.shrink(p - reinterpret_cast(buffer.data())); return SharedBuffer::create(WTFMove(buffer)); } } // namespace WebCore