aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-01-04 18:55:01 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-01-04 18:55:16 -0800
commit3b259211df10e06ddf5844898a6ffa3c9679e4d8 (patch)
treecdc53cc60d9b7db0ba123a62b614bf0d40b50964 /src/bun.js
parent6b777c4b0b6ea0ef961c2acf5e0483fcb52bf1e7 (diff)
downloadbun-3b259211df10e06ddf5844898a6ffa3c9679e4d8.tar.gz
bun-3b259211df10e06ddf5844898a6ffa3c9679e4d8.tar.zst
bun-3b259211df10e06ddf5844898a6ffa3c9679e4d8.zip
Add `SharedBuffer` from WebKit to make it easier to import more WebCore stuff
Diffstat (limited to 'src/bun.js')
-rw-r--r--src/bun.js/bindings/webcore/SharedBuffer.cpp689
-rw-r--r--src/bun.js/bindings/webcore/SharedBuffer.h422
2 files changed, 1111 insertions, 0 deletions
diff --git a/src/bun.js/bindings/webcore/SharedBuffer.cpp b/src/bun.js/bindings/webcore/SharedBuffer.cpp
new file mode 100644
index 000000000..6ade267de
--- /dev/null
+++ b/src/bun.js/bindings/webcore/SharedBuffer.cpp
@@ -0,0 +1,689 @@
+/*
+ * 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 <JavaScriptCore/ArrayBuffer.h>
+#include <algorithm>
+#include <wtf/HexNumber.h>
+#include <wtf/persistence/PersistentCoders.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/unicode/UTF8Conversion.h>
+
+namespace WebCore {
+
+Ref<FragmentedSharedBuffer> FragmentedSharedBuffer::create()
+{
+ return adoptRef(*new FragmentedSharedBuffer);
+}
+
+Ref<FragmentedSharedBuffer> FragmentedSharedBuffer::create(const uint8_t* data, size_t size)
+{
+ return adoptRef(*new FragmentedSharedBuffer(data, size));
+}
+
+Ref<FragmentedSharedBuffer> FragmentedSharedBuffer::create(FileSystem::MappedFileData&& mappedFileData)
+{
+ return adoptRef(*new FragmentedSharedBuffer(WTFMove(mappedFileData)));
+}
+
+Ref<FragmentedSharedBuffer> FragmentedSharedBuffer::create(Ref<SharedBuffer>&& buffer)
+{
+ return adoptRef(*new FragmentedSharedBuffer(WTFMove(buffer)));
+}
+
+Ref<FragmentedSharedBuffer> FragmentedSharedBuffer::create(Vector<uint8_t>&& vector)
+{
+ return adoptRef(*new FragmentedSharedBuffer(WTFMove(vector)));
+}
+
+Ref<FragmentedSharedBuffer> FragmentedSharedBuffer::create(DataSegment::Provider&& provider)
+{
+ return adoptRef(*new FragmentedSharedBuffer(WTFMove(provider)));
+}
+
+FragmentedSharedBuffer::FragmentedSharedBuffer() = default;
+
+FragmentedSharedBuffer::FragmentedSharedBuffer(FileSystem::MappedFileData&& fileData)
+ : m_size(fileData.size())
+{
+ m_segments.append({ 0, DataSegment::create(WTFMove(fileData)) });
+}
+
+FragmentedSharedBuffer::FragmentedSharedBuffer(DataSegment::Provider&& provider)
+ : m_size(provider.size())
+{
+ m_segments.append({ 0, DataSegment::create(WTFMove(provider)) });
+}
+
+FragmentedSharedBuffer::FragmentedSharedBuffer(Ref<SharedBuffer>&& buffer)
+{
+ append(WTFMove(buffer));
+}
+
+#if USE(GSTREAMER)
+Ref<FragmentedSharedBuffer> FragmentedSharedBuffer::create(GstMappedOwnedBuffer& mappedBuffer)
+{
+ return adoptRef(*new FragmentedSharedBuffer(mappedBuffer));
+}
+
+FragmentedSharedBuffer::FragmentedSharedBuffer(GstMappedOwnedBuffer& mappedBuffer)
+ : m_size(mappedBuffer.size())
+{
+ m_segments.append({ 0, DataSegment::create(&mappedBuffer) });
+}
+#endif
+
+static Vector<uint8_t> combineSegmentsData(const FragmentedSharedBuffer::DataSegmentVector& segments, size_t size)
+{
+ Vector<uint8_t> combinedData;
+ combinedData.reserveInitialCapacity(size);
+ for (auto& segment : segments)
+ combinedData.append(segment.segment->data(), segment.segment->size());
+ ASSERT(combinedData.size() == size);
+ return combinedData;
+}
+
+Ref<SharedBuffer> FragmentedSharedBuffer::makeContiguous() const
+{
+ if (m_contiguous)
+ return Ref { *static_cast<SharedBuffer*>(const_cast<FragmentedSharedBuffer*>(this)) };
+ if (!m_segments.size())
+ return SharedBuffer::create();
+ if (m_segments.size() == 1)
+ return SharedBuffer::create(m_segments[0].segment.copyRef());
+ auto combinedData = combineSegmentsData(m_segments, m_size);
+ return SharedBuffer::create(WTFMove(combinedData));
+}
+
+Vector<uint8_t> FragmentedSharedBuffer::copyData() const
+{
+ Vector<uint8_t> data;
+ data.reserveInitialCapacity(size());
+ forEachSegment([&data](auto& span) {
+ data.uncheckedAppend(span);
+ });
+ return data;
+}
+
+Vector<uint8_t> FragmentedSharedBuffer::takeData()
+{
+ if (m_segments.isEmpty())
+ return {};
+
+ Vector<uint8_t> combinedData;
+ if (hasOneSegment() && std::holds_alternative<Vector<uint8_t>>(m_segments[0].segment->m_immutableData) && m_segments[0].segment->hasOneRef())
+ combinedData = std::exchange(std::get<Vector<uint8_t>>(const_cast<DataSegment&>(m_segments[0].segment.get()).m_immutableData), Vector<uint8_t>());
+ else
+ combinedData = combineSegmentsData(m_segments, m_size);
+
+ clear();
+ return combinedData;
+}
+
+SharedBufferDataView FragmentedSharedBuffer::getSomeData(size_t position) const
+{
+ const DataSegmentVectorEntry* element = getSegmentForPosition(position);
+ return { element->segment.copyRef(), position - element->beginPosition };
+}
+
+Ref<SharedBuffer> FragmentedSharedBuffer::getContiguousData(size_t position, size_t length) const
+{
+ if (position >= m_size)
+ return SharedBuffer::create();
+ length = std::min(m_size - position, length);
+ const DataSegmentVectorEntry* element = getSegmentForPosition(position);
+ size_t offsetInSegment = position - element->beginPosition;
+ ASSERT(element->segment->size() > offsetInSegment);
+ if (element->segment->size() - offsetInSegment >= length)
+ return SharedBufferDataView { element->segment.copyRef(), offsetInSegment, length }.createSharedBuffer();
+ Vector<uint8_t> combinedData;
+ combinedData.reserveInitialCapacity(length);
+ combinedData.append(element->segment->data() + offsetInSegment, element->segment->size() - offsetInSegment);
+ for (++element; combinedData.size() < length && element != m_segments.end(); element++) {
+ auto canCopy = std::min(length - combinedData.size(), element->segment->size());
+ combinedData.append(element->segment->data(), canCopy);
+ }
+ return SharedBuffer::create(WTFMove(combinedData));
+}
+
+const FragmentedSharedBuffer::DataSegmentVectorEntry* FragmentedSharedBuffer::getSegmentForPosition(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;
+}
+
+String FragmentedSharedBuffer::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<ArrayBuffer> FragmentedSharedBuffer::tryCreateArrayBuffer() const
+{
+ auto arrayBuffer = ArrayBuffer::tryCreateUninitialized(static_cast<unsigned>(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<uint8_t*>(arrayBuffer->data()) + position, segment.segment->data(), segment.segment->size());
+ position += segment.segment->size();
+ }
+
+ ASSERT(position == m_size);
+ ASSERT(internallyConsistent());
+ return arrayBuffer;
+}
+
+void FragmentedSharedBuffer::append(const FragmentedSharedBuffer& data)
+{
+ ASSERT(!m_contiguous);
+ 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 FragmentedSharedBuffer::append(const uint8_t* data, size_t length)
+{
+ ASSERT(!m_contiguous);
+ m_segments.append({ m_size, DataSegment::create(Vector { data, length }) });
+ m_size += length;
+ ASSERT(internallyConsistent());
+}
+
+void FragmentedSharedBuffer::append(Vector<uint8_t>&& data)
+{
+ ASSERT(!m_contiguous);
+ auto dataSize = data.size();
+ m_segments.append({ m_size, DataSegment::create(WTFMove(data)) });
+ m_size += dataSize;
+ ASSERT(internallyConsistent());
+}
+
+void FragmentedSharedBuffer::clear()
+{
+ m_size = 0;
+ m_segments.clear();
+ ASSERT(internallyConsistent());
+}
+
+Ref<FragmentedSharedBuffer> FragmentedSharedBuffer::copy() const
+{
+ if (m_contiguous)
+ return m_segments.size() ? SharedBuffer::create(m_segments[0].segment.copyRef()) : SharedBuffer::create();
+ Ref<FragmentedSharedBuffer> clone = adoptRef(*new FragmentedSharedBuffer);
+ 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 FragmentedSharedBuffer::forEachSegment(const Function<void(const Span<const uint8_t>&)>& apply) const
+{
+ auto segments = m_segments;
+ for (auto& segment : segments)
+ segment.segment->iterate(apply);
+}
+
+void DataSegment::iterate(const Function<void(const Span<const uint8_t>&)>& apply) const
+{
+#if USE(FOUNDATION)
+ if (auto* data = std::get_if<RetainPtr<CFDataRef>>(&m_immutableData))
+ return iterate(data->get(), apply);
+#endif
+ apply({ data(), size() });
+}
+
+void FragmentedSharedBuffer::forEachSegmentAsSharedBuffer(const Function<void(Ref<SharedBuffer>&&)>& apply) const
+{
+ auto protectedThis = Ref { *this };
+ for (auto& segment : m_segments)
+ apply(SharedBuffer::create(segment.segment.copyRef()));
+}
+
+bool FragmentedSharedBuffer::startsWith(const Span<const uint8_t>& 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;
+}
+
+Vector<uint8_t> FragmentedSharedBuffer::read(size_t offset, size_t length) const
+{
+ Vector<uint8_t> data;
+ if (offset >= size())
+ return data;
+ auto remaining = std::min(length, size() - offset);
+ if (!remaining)
+ return data;
+
+ data.reserveInitialCapacity(remaining);
+ auto* currentSegment = getSegmentForPosition(offset);
+ size_t offsetInSegment = offset - currentSegment->beginPosition;
+ size_t availableInSegment = std::min(currentSegment->segment->size() - offsetInSegment, remaining);
+ data.append(currentSegment->segment->data() + offsetInSegment, availableInSegment);
+
+ remaining -= availableInSegment;
+
+ auto* afterLastSegment = end();
+
+ while (remaining && ++currentSegment != afterLastSegment) {
+ size_t lengthInSegment = std::min(currentSegment->segment->size(), remaining);
+ data.append(currentSegment->segment->data(), lengthInSegment);
+ remaining -= lengthInSegment;
+ }
+ return data;
+}
+
+void FragmentedSharedBuffer::copyTo(void* destination, size_t length) const
+{
+ return copyTo(destination, 0, length);
+}
+
+void FragmentedSharedBuffer::copyTo(void* destination, size_t offset, size_t length) const
+{
+ ASSERT(length + offset <= size());
+ if (offset >= size())
+ return;
+ auto remaining = std::min(length, size() - offset);
+ if (!remaining)
+ return;
+
+ auto segment = begin();
+ if (offset >= segment->segment->size()) {
+ auto comparator = [](const size_t& position, const DataSegmentVectorEntry& entry) {
+ return position < entry.beginPosition;
+ };
+ segment = std::upper_bound(segment, end(), offset, comparator);
+ segment--; // std::upper_bound gives a pointer to the segment that is greater than offset. We want the segment just before that.
+ }
+ auto destinationPtr = static_cast<uint8_t*>(destination);
+
+ size_t positionInSegment = offset - segment->beginPosition;
+ size_t amountToCopyThisTime = std::min(remaining, segment->segment->size() - positionInSegment);
+ memcpy(destinationPtr, segment->segment->data() + positionInSegment, amountToCopyThisTime);
+ remaining -= amountToCopyThisTime;
+ if (!remaining)
+ return;
+ destinationPtr += amountToCopyThisTime;
+
+ // If we reach here, there must be at least another segment available as we have content left to be fetched.
+ for (++segment; segment != end(); ++segment) {
+ size_t amountToCopyThisTime = std::min(remaining, segment->segment->size());
+ memcpy(destinationPtr, segment->segment->data(), amountToCopyThisTime);
+ remaining -= amountToCopyThisTime;
+ if (!remaining)
+ return;
+ destinationPtr += amountToCopyThisTime;
+ }
+}
+
+#if ASSERT_ENABLED
+bool FragmentedSharedBuffer::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
+
+#if !USE(CF)
+void FragmentedSharedBuffer::hintMemoryNotNeededSoon() const
+{
+}
+#endif
+
+bool FragmentedSharedBuffer::operator==(const FragmentedSharedBuffer& 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;
+}
+
+SharedBuffer::SharedBuffer()
+{
+ m_contiguous = true;
+}
+
+SharedBuffer::SharedBuffer(Ref<const DataSegment>&& segment)
+{
+ m_size = segment->size();
+ m_segments.append({ 0, WTFMove(segment) });
+ m_contiguous = true;
+}
+
+SharedBuffer::SharedBuffer(Ref<FragmentedSharedBuffer>&& contiguousBuffer)
+{
+ ASSERT(contiguousBuffer->hasOneSegment() || contiguousBuffer->isEmpty());
+ m_size = contiguousBuffer->size();
+ if (contiguousBuffer->hasOneSegment())
+ m_segments.append({ 0, contiguousBuffer->m_segments[0].segment.copyRef() });
+ m_contiguous = true;
+}
+
+SharedBuffer::SharedBuffer(FileSystem::MappedFileData&& data)
+ : FragmentedSharedBuffer(WTFMove(data))
+{
+ m_contiguous = true;
+}
+
+RefPtr<SharedBuffer> 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)));
+ }
+
+ auto buffer = FileSystem::readEntireFile(filePath);
+ if (!buffer)
+ return nullptr;
+
+ return SharedBuffer::create(WTFMove(*buffer));
+}
+
+const uint8_t* SharedBuffer::data() const
+{
+ if (m_segments.isEmpty())
+ return nullptr;
+ return m_segments[0].segment->data();
+}
+
+WTF::Persistence::Decoder SharedBuffer::decoder() const
+{
+ return { { data(), size() } };
+}
+
+Ref<DataSegment> DataSegment::create(Vector<uint8_t>&& data)
+{
+ data.shrinkToFit();
+ return adoptRef(*new DataSegment(WTFMove(data)));
+}
+
+#if USE(CF)
+Ref<DataSegment> DataSegment::create(RetainPtr<CFDataRef>&& data)
+{
+ return adoptRef(*new DataSegment(WTFMove(data)));
+}
+#endif
+
+#if USE(GLIB)
+Ref<DataSegment> DataSegment::create(GRefPtr<GBytes>&& data)
+{
+ return adoptRef(*new DataSegment(WTFMove(data)));
+}
+#endif
+
+#if USE(GSTREAMER)
+Ref<DataSegment> DataSegment::create(RefPtr<GstMappedOwnedBuffer>&& data)
+{
+ return adoptRef(*new DataSegment(WTFMove(data)));
+}
+#endif
+
+Ref<DataSegment> DataSegment::create(FileSystem::MappedFileData&& data)
+{
+ return adoptRef(*new DataSegment(WTFMove(data)));
+}
+
+Ref<DataSegment> DataSegment::create(Provider&& provider)
+{
+ return adoptRef(*new DataSegment(WTFMove(provider)));
+}
+
+const uint8_t* DataSegment::data() const
+{
+ auto visitor = WTF::makeVisitor(
+ [](const Vector<uint8_t>& data) -> const uint8_t* { return data.data(); },
+#if USE(CF)
+ [](const RetainPtr<CFDataRef>& data) -> const uint8_t* { return CFDataGetBytePtr(data.get()); },
+#endif
+#if USE(GLIB)
+ [](const GRefPtr<GBytes>& data) -> const uint8_t* { return static_cast<const uint8_t*>(g_bytes_get_data(data.get(), nullptr)); },
+#endif
+#if USE(GSTREAMER)
+ [](const RefPtr<GstMappedOwnedBuffer>& data) -> const uint8_t* { return data->data(); },
+#endif
+ [](const FileSystem::MappedFileData& data) -> const uint8_t* { return static_cast<const uint8_t*>(data.data()); },
+ [](const Provider& provider) -> const uint8_t* { return provider.data(); });
+ return std::visit(visitor, m_immutableData);
+}
+
+bool DataSegment::containsMappedFileData() const
+{
+ return std::holds_alternative<FileSystem::MappedFileData>(m_immutableData);
+}
+
+size_t DataSegment::size() const
+{
+ auto visitor = WTF::makeVisitor(
+ [](const Vector<uint8_t>& data) -> size_t { return data.size(); },
+#if USE(CF)
+ [](const RetainPtr<CFDataRef>& data) -> size_t { return CFDataGetLength(data.get()); },
+#endif
+#if USE(GLIB)
+ [](const GRefPtr<GBytes>& data) -> size_t { return g_bytes_get_size(data.get()); },
+#endif
+#if USE(GSTREAMER)
+ [](const RefPtr<GstMappedOwnedBuffer>& data) -> size_t { return data->size(); },
+#endif
+ [](const FileSystem::MappedFileData& data) -> size_t { return data.size(); },
+ [](const Provider& provider) -> size_t { return provider.size(); });
+ return std::visit(visitor, m_immutableData);
+}
+
+SharedBufferBuilder::SharedBufferBuilder(RefPtr<FragmentedSharedBuffer>&& buffer)
+{
+ if (!buffer)
+ return;
+ initialize(buffer.releaseNonNull());
+}
+
+SharedBufferBuilder& SharedBufferBuilder::operator=(RefPtr<FragmentedSharedBuffer>&& buffer)
+{
+ if (!buffer) {
+ m_buffer = nullptr;
+ return *this;
+ }
+ m_buffer = nullptr;
+ initialize(buffer.releaseNonNull());
+ return *this;
+}
+
+void SharedBufferBuilder::initialize(Ref<FragmentedSharedBuffer>&& buffer)
+{
+ ASSERT(!m_buffer);
+ // We do not want to take a reference to the SharedBuffer as all SharedBuffer should be immutable
+ // once created.
+ if (buffer->hasOneRef() && !buffer->isContiguous()) {
+ m_buffer = WTFMove(buffer);
+ return;
+ }
+ append(buffer);
+}
+
+RefPtr<ArrayBuffer> SharedBufferBuilder::tryCreateArrayBuffer() const
+{
+ return m_buffer ? m_buffer->tryCreateArrayBuffer() : ArrayBuffer::tryCreate(nullptr, 0);
+}
+
+Ref<FragmentedSharedBuffer> SharedBufferBuilder::take()
+{
+ return m_buffer ? m_buffer.releaseNonNull() : FragmentedSharedBuffer::create();
+}
+
+Ref<SharedBuffer> SharedBufferBuilder::takeAsContiguous()
+{
+ return take()->makeContiguous();
+}
+
+RefPtr<ArrayBuffer> SharedBufferBuilder::takeAsArrayBuffer()
+{
+ if (!m_buffer)
+ return ArrayBuffer::tryCreate(nullptr, 0);
+ return take()->tryCreateArrayBuffer();
+}
+
+void SharedBufferBuilder::ensureBuffer()
+{
+ if (!m_buffer)
+ m_buffer = FragmentedSharedBuffer::create();
+}
+
+SharedBufferDataView::SharedBufferDataView(Ref<const DataSegment>&& segment, size_t positionWithinSegment, std::optional<size_t> size)
+ : m_segment(WTFMove(segment))
+ , m_positionWithinSegment(positionWithinSegment)
+ , m_size(size ? *size : m_segment->size() - positionWithinSegment)
+{
+ RELEASE_ASSERT(m_positionWithinSegment < m_segment->size());
+ RELEASE_ASSERT(m_size <= m_segment->size() - m_positionWithinSegment);
+}
+
+SharedBufferDataView::SharedBufferDataView(const SharedBufferDataView& other, size_t newSize)
+ : SharedBufferDataView(other.m_segment.copyRef(), other.m_positionWithinSegment, newSize)
+{
+}
+
+Ref<SharedBuffer> SharedBufferDataView::createSharedBuffer() const
+{
+ const Ref<const DataSegment> segment = m_segment;
+ return SharedBuffer::create(DataSegment::Provider {
+ [segment, data = data()]() { return data; },
+ [size = size()]() { return size; } });
+}
+
+RefPtr<SharedBuffer> 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<size_t>::max() / 3) {
+ if (length > std::numeric_limits<size_t>::max() / 3)
+ return nullptr;
+ }
+
+ Vector<uint8_t> buffer(length * 3);
+
+ // Convert to runs of 8-bit characters.
+ char* p = reinterpret_cast<char*>(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::ConversionResult::Success)
+ return nullptr;
+ }
+ }
+
+ buffer.shrink(p - reinterpret_cast<char*>(buffer.data()));
+ return SharedBuffer::create(WTFMove(buffer));
+}
+
+} // namespace WebCore
diff --git a/src/bun.js/bindings/webcore/SharedBuffer.h b/src/bun.js/bindings/webcore/SharedBuffer.h
new file mode 100644
index 000000000..2c5051dab
--- /dev/null
+++ b/src/bun.js/bindings/webcore/SharedBuffer.h
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2006-2021 Apple Inc. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2009-2010. 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.
+ */
+
+#pragma once
+
+#include <JavaScriptCore/Forward.h>
+#include <utility>
+#include <variant>
+#include <wtf/FileSystem.h>
+#include <wtf/Forward.h>
+#include <wtf/Function.h>
+#include <wtf/Span.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/TypeCasts.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+#if USE(CF)
+#include <wtf/RetainPtr.h>
+#endif
+
+#if USE(GLIB)
+#include <wtf/glib/GRefPtr.h>
+typedef struct _GBytes GBytes;
+#endif
+
+#if USE(GSTREAMER)
+#include "GStreamerCommon.h"
+#endif
+
+#if USE(FOUNDATION)
+OBJC_CLASS NSArray;
+OBJC_CLASS NSData;
+typedef struct OpaqueCMBlockBuffer *CMBlockBufferRef;
+#endif
+
+namespace WTF {
+namespace Persistence {
+class Decoder;
+}
+}
+
+namespace WebCore {
+
+class SharedBuffer;
+class SharedBufferDataView;
+
+// Data wrapped by a DataSegment should be immutable because it can be referenced by other objects.
+// To modify or combine the data, allocate a new DataSegment.
+class DataSegment : public ThreadSafeRefCounted<DataSegment> {
+public:
+ WEBCORE_EXPORT const uint8_t *data() const;
+ WEBCORE_EXPORT size_t size() const;
+
+ WEBCORE_EXPORT static Ref<DataSegment> create(Vector<uint8_t> &&);
+
+#if USE(CF)
+ WEBCORE_EXPORT static Ref<DataSegment> create(RetainPtr<CFDataRef> &&);
+#endif
+#if USE(GLIB)
+ WEBCORE_EXPORT static Ref<DataSegment> create(GRefPtr<GBytes> &&);
+#endif
+#if USE(GSTREAMER)
+ WEBCORE_EXPORT static Ref<DataSegment> create(RefPtr<GstMappedOwnedBuffer> &&);
+#endif
+ WEBCORE_EXPORT static Ref<DataSegment> create(FileSystem::MappedFileData &&);
+
+ struct Provider {
+ Function<const uint8_t *()> data;
+ Function<size_t()> size;
+ };
+
+ WEBCORE_EXPORT static Ref<DataSegment> create(Provider &&);
+
+#if USE(FOUNDATION)
+ WEBCORE_EXPORT RetainPtr<NSData> createNSData() const;
+#endif
+
+ WEBCORE_EXPORT bool containsMappedFileData() const;
+
+private:
+ void iterate(const Function<void(const Span<const uint8_t> &)> &apply) const;
+#if USE(FOUNDATION)
+ void iterate(CFDataRef, const Function<void(const Span<const uint8_t> &)> &apply) const;
+#endif
+
+ explicit DataSegment(Vector<uint8_t> &&data)
+ : m_immutableData(WTFMove(data))
+ {
+ }
+#if USE(CF)
+ explicit DataSegment(RetainPtr<CFDataRef> &&data)
+ : m_immutableData(WTFMove(data))
+ {
+ }
+#endif
+#if USE(GLIB)
+ explicit DataSegment(GRefPtr<GBytes> &&data)
+ : m_immutableData(WTFMove(data))
+ {
+ }
+#endif
+#if USE(GSTREAMER)
+ explicit DataSegment(RefPtr<GstMappedOwnedBuffer> &&data)
+ : m_immutableData(WTFMove(data))
+ {
+ }
+#endif
+ explicit DataSegment(FileSystem::MappedFileData &&data)
+ : m_immutableData(WTFMove(data))
+ {
+ }
+ explicit DataSegment(Provider &&provider)
+ : m_immutableData(WTFMove(provider))
+ {
+ }
+
+ std::variant<Vector<uint8_t>,
+#if USE(CF)
+ RetainPtr<CFDataRef>,
+#endif
+#if USE(GLIB)
+ GRefPtr<GBytes>,
+#endif
+#if USE(GSTREAMER)
+ RefPtr<GstMappedOwnedBuffer>,
+#endif
+ FileSystem::MappedFileData,
+ Provider>
+ m_immutableData;
+ friend class FragmentedSharedBuffer;
+ friend class SharedBuffer; // For createCFData
+};
+
+class FragmentedSharedBuffer : public ThreadSafeRefCounted<FragmentedSharedBuffer> {
+public:
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create();
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create(const uint8_t *, size_t);
+ static Ref<FragmentedSharedBuffer> create(const char *data, size_t size) { return create(reinterpret_cast<const uint8_t *>(data), size); }
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create(FileSystem::MappedFileData &&);
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create(Ref<SharedBuffer> &&);
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create(Vector<uint8_t> &&);
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create(DataSegment::Provider &&);
+
+#if USE(FOUNDATION)
+ WEBCORE_EXPORT RetainPtr<NSArray> createNSDataArray() const;
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create(NSData *);
+ WEBCORE_EXPORT RetainPtr<CMBlockBufferRef> createCMBlockBuffer() const;
+#endif
+#if USE(CF)
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create(CFDataRef);
+#endif
+
+#if USE(GLIB)
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create(GBytes *);
+#endif
+
+#if USE(GSTREAMER)
+ WEBCORE_EXPORT static Ref<FragmentedSharedBuffer> create(GstMappedOwnedBuffer &);
+#endif
+ WEBCORE_EXPORT Vector<uint8_t> copyData() const;
+ WEBCORE_EXPORT Vector<uint8_t> read(size_t offset, size_t length) const;
+
+ // Similar to copyData() but avoids copying and will take the data instead when it is safe (The FragmentedSharedBuffer is not shared).
+ Vector<uint8_t> extractData();
+
+ WEBCORE_EXPORT RefPtr<ArrayBuffer> tryCreateArrayBuffer() const;
+
+ size_t size() const { return m_size; }
+ bool isEmpty() const { return !size(); }
+ bool isContiguous() const { return m_contiguous; }
+
+ WEBCORE_EXPORT Ref<FragmentedSharedBuffer> copy() const;
+ WEBCORE_EXPORT void copyTo(void *destination, size_t length) const;
+ WEBCORE_EXPORT void copyTo(void *destination, size_t offset, size_t length) const;
+
+ WEBCORE_EXPORT void forEachSegment(const Function<void(const Span<const uint8_t> &)> &) const;
+ WEBCORE_EXPORT bool startsWith(const Span<const uint8_t> &prefix) const;
+ WEBCORE_EXPORT void forEachSegmentAsSharedBuffer(const Function<void(Ref<SharedBuffer> &&)> &) const;
+
+ using DataSegment = WebCore::DataSegment; // To keep backward compatibility when using FragmentedSharedBuffer::DataSegment
+
+ struct DataSegmentVectorEntry {
+ size_t beginPosition;
+ const Ref<const DataSegment> segment;
+ };
+ using DataSegmentVector = Vector<DataSegmentVectorEntry, 1>;
+ DataSegmentVector::const_iterator begin() const { return m_segments.begin(); }
+ DataSegmentVector::const_iterator end() const { return m_segments.end(); }
+ bool hasOneSegment() const { return m_segments.size() == 1; }
+
+ // begin and end take O(1) time, this takes O(log(N)) time.
+ WEBCORE_EXPORT SharedBufferDataView getSomeData(size_t position) const;
+ WEBCORE_EXPORT Ref<SharedBuffer> getContiguousData(size_t position, size_t length) const;
+
+ WEBCORE_EXPORT String toHexString() const;
+
+ void hintMemoryNotNeededSoon() const;
+
+ WEBCORE_EXPORT bool operator==(const FragmentedSharedBuffer &) const;
+ bool operator!=(const FragmentedSharedBuffer &other) const { return !operator==(other); }
+
+ WEBCORE_EXPORT Ref<SharedBuffer> makeContiguous() const;
+
+protected:
+ friend class SharedBuffer;
+
+ DataSegmentVector m_segments;
+ bool m_contiguous { false };
+
+ WEBCORE_EXPORT FragmentedSharedBuffer();
+ explicit FragmentedSharedBuffer(const uint8_t *data, size_t size) { append(data, size); }
+ explicit FragmentedSharedBuffer(const char *data, size_t size) { append(data, size); }
+ explicit FragmentedSharedBuffer(Vector<uint8_t> &&data) { append(WTFMove(data)); }
+ WEBCORE_EXPORT explicit FragmentedSharedBuffer(FileSystem::MappedFileData &&);
+ WEBCORE_EXPORT explicit FragmentedSharedBuffer(DataSegment::Provider &&);
+ WEBCORE_EXPORT explicit FragmentedSharedBuffer(Ref<SharedBuffer> &&);
+#if USE(CF)
+ WEBCORE_EXPORT explicit FragmentedSharedBuffer(CFDataRef);
+#endif
+#if USE(GLIB)
+ WEBCORE_EXPORT explicit FragmentedSharedBuffer(GBytes *);
+#endif
+#if USE(GSTREAMER)
+ WEBCORE_EXPORT explicit FragmentedSharedBuffer(GstMappedOwnedBuffer &);
+#endif
+ size_t m_size { 0 };
+
+private:
+ friend class SharedBufferBuilder;
+ WEBCORE_EXPORT void append(const FragmentedSharedBuffer &);
+ WEBCORE_EXPORT void append(const uint8_t *, size_t);
+ void append(Span<const uint8_t> value) { append(value.data(), value.size()); }
+ void append(const char *data, size_t length) { append(reinterpret_cast<const uint8_t *>(data), length); }
+ WEBCORE_EXPORT void append(Vector<uint8_t> &&);
+#if USE(FOUNDATION)
+ WEBCORE_EXPORT void append(NSData *);
+#endif
+#if USE(CF)
+ WEBCORE_EXPORT void append(CFDataRef);
+#endif
+
+ WEBCORE_EXPORT void clear();
+
+ // Combines all the segments into a Vector and returns that vector after clearing the FragmentedSharedBuffer.
+ WEBCORE_EXPORT Vector<uint8_t> takeData();
+ const DataSegmentVectorEntry *getSegmentForPosition(size_t position) const;
+
+#if ASSERT_ENABLED
+ bool internallyConsistent() const;
+#endif
+};
+
+// A SharedBuffer is a FragmentedSharedBuffer that allows to directly access its content via the data() and related methods.
+class SharedBuffer : public FragmentedSharedBuffer {
+public:
+ template<typename... Args>
+ static Ref<SharedBuffer> create(Args &&...args)
+ {
+ if constexpr (!sizeof...(Args))
+ return adoptRef(*new SharedBuffer());
+ else if constexpr (sizeof...(Args) == 1
+ && (std::is_same_v<Args, Ref<const DataSegment>> && ...))
+ return adoptRef(*new SharedBuffer(std::forward<Args>(args)...));
+ else if constexpr (sizeof...(Args) == 1
+ && (std::is_same_v<std::remove_cvref_t<Args>, DataSegment> && ...))
+ return adoptRef(*new SharedBuffer(std::forward<Args>(args)...));
+ else {
+ auto buffer = FragmentedSharedBuffer::create(std::forward<Args>(args)...);
+ return adoptRef(*new SharedBuffer(WTFMove(buffer)));
+ }
+ }
+
+ WEBCORE_EXPORT const uint8_t *data() const;
+ const char *dataAsCharPtr() const { return reinterpret_cast<const char *>(data()); }
+ Span<const uint8_t> dataAsSpanForContiguousData() const { return Span(data(), isContiguous() ? size() : 0); }
+ WTF::Persistence::Decoder decoder() const;
+
+ enum class MayUseFileMapping : bool { No,
+ Yes };
+ WEBCORE_EXPORT static RefPtr<SharedBuffer> createWithContentsOfFile(const String &filePath, FileSystem::MappedFileMode = FileSystem::MappedFileMode::Shared, MayUseFileMapping = MayUseFileMapping::Yes);
+
+#if USE(FOUNDATION)
+ WEBCORE_EXPORT RetainPtr<NSData> createNSData() const;
+#endif
+#if USE(CF)
+ WEBCORE_EXPORT RetainPtr<CFDataRef> createCFData() const;
+#endif
+#if USE(GLIB)
+ WEBCORE_EXPORT GRefPtr<GBytes> createGBytes() const;
+#endif
+
+private:
+ WEBCORE_EXPORT SharedBuffer();
+ SharedBuffer(const DataSegment &segment)
+ : SharedBuffer(Ref<const DataSegment> { segment })
+ {
+ }
+ WEBCORE_EXPORT explicit SharedBuffer(FileSystem::MappedFileData &&);
+ WEBCORE_EXPORT explicit SharedBuffer(Ref<const DataSegment> &&);
+ WEBCORE_EXPORT explicit SharedBuffer(Ref<FragmentedSharedBuffer> &&);
+
+ WEBCORE_EXPORT static RefPtr<SharedBuffer> createFromReadingFile(const String &filePath);
+};
+
+class SharedBufferBuilder {
+ WTF_MAKE_FAST_ALLOCATED;
+
+public:
+ SharedBufferBuilder() = default;
+ SharedBufferBuilder(SharedBufferBuilder &&) = default;
+ WEBCORE_EXPORT explicit SharedBufferBuilder(RefPtr<FragmentedSharedBuffer> &&);
+ explicit SharedBufferBuilder(Ref<FragmentedSharedBuffer> &&buffer) { initialize(WTFMove(buffer)); }
+ explicit SharedBufferBuilder(RefPtr<SharedBuffer> &&buffer)
+ : SharedBufferBuilder(RefPtr<FragmentedSharedBuffer> { WTFMove(buffer) })
+ {
+ }
+ explicit SharedBufferBuilder(Ref<SharedBuffer> &&buffer) { initialize(WTFMove(buffer)); }
+
+ template<typename... Args>
+ SharedBufferBuilder(std::in_place_t, Args &&...args)
+ : m_buffer(FragmentedSharedBuffer::create(std::forward<Args>(args)...))
+ {
+ }
+
+ SharedBufferBuilder &operator=(SharedBufferBuilder &&) = default;
+ WEBCORE_EXPORT SharedBufferBuilder &operator=(RefPtr<FragmentedSharedBuffer> &&);
+
+ template<typename... Args>
+ void append(Args &&...args)
+ {
+ ensureBuffer();
+ m_buffer->append(std::forward<Args>(args)...);
+ }
+
+ explicit operator bool() const { return !isNull(); }
+ bool isNull() const { return !m_buffer; }
+ bool isEmpty() const { return m_buffer ? m_buffer->isEmpty() : true; }
+
+ size_t size() const { return m_buffer ? m_buffer->size() : 0; }
+
+ void reset() { m_buffer = nullptr; }
+ void empty() { m_buffer = FragmentedSharedBuffer::create(); }
+
+ RefPtr<FragmentedSharedBuffer> get() const { return m_buffer; }
+ Ref<FragmentedSharedBuffer> copy() const { return m_buffer ? m_buffer->copy() : FragmentedSharedBuffer::create(); }
+ WEBCORE_EXPORT RefPtr<ArrayBuffer> tryCreateArrayBuffer() const;
+
+ WEBCORE_EXPORT Ref<FragmentedSharedBuffer> take();
+ WEBCORE_EXPORT Ref<SharedBuffer> takeAsContiguous();
+ WEBCORE_EXPORT RefPtr<ArrayBuffer> takeAsArrayBuffer();
+
+private:
+ friend class ScriptBuffer;
+ friend class FetchBodyConsumer;
+ // Copy constructor should make a copy of the underlying SharedBuffer
+ // This is prevented by ScriptBuffer and FetchBodyConsumer classes (bug 234215)
+ // For now let the default constructor/operator take a reference to the
+ // SharedBuffer.
+ SharedBufferBuilder(const SharedBufferBuilder &) = default;
+ SharedBufferBuilder &operator=(const SharedBufferBuilder &) = default;
+
+ WEBCORE_EXPORT void initialize(Ref<FragmentedSharedBuffer> &&);
+ WEBCORE_EXPORT void ensureBuffer();
+ RefPtr<FragmentedSharedBuffer> m_buffer;
+};
+
+inline Vector<uint8_t> FragmentedSharedBuffer::extractData()
+{
+ if (hasOneRef())
+ return takeData();
+ return copyData();
+}
+
+class SharedBufferDataView {
+public:
+ WEBCORE_EXPORT SharedBufferDataView(Ref<const DataSegment> &&, size_t positionWithinSegment, std::optional<size_t> newSize = std::nullopt);
+ WEBCORE_EXPORT SharedBufferDataView(const SharedBufferDataView &, size_t newSize);
+ size_t size() const { return m_size; }
+ const uint8_t *data() const { return m_segment->data() + m_positionWithinSegment; }
+ const char *dataAsCharPtr() const { return reinterpret_cast<const char *>(data()); }
+
+ WEBCORE_EXPORT Ref<SharedBuffer> createSharedBuffer() const;
+#if USE(FOUNDATION)
+ WEBCORE_EXPORT RetainPtr<NSData> createNSData() const;
+#endif
+private:
+ const Ref<const DataSegment> m_segment;
+ const size_t m_positionWithinSegment;
+ const size_t m_size;
+};
+
+RefPtr<SharedBuffer> utf8Buffer(const String &);
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::SharedBuffer)
+static bool isType(const WebCore::FragmentedSharedBuffer &buffer) { return buffer.isContiguous(); }
+SPECIALIZE_TYPE_TRAITS_END()