tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit be6b5737dce2b1f0de100036026dc92c39a01189
parent ce1cb61ef9dcc89f90cc41a4c6e6526cc9852415
Author: Matthew Gaudet <mgaudet@mozilla.com>
Date:   Mon,  3 Nov 2025 21:01:29 +0000

Bug 1989115 - Add FixedBufferPrinter r=mgaudet

Differential Revision: https://phabricator.services.mozilla.com/D269788

Diffstat:
Mjs/public/Printer.h | 22++++++++++++++++++++++
Mjs/src/jsapi-tests/moz.build | 1+
Ajs/src/jsapi-tests/testPrinter.cpp | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/vm/Printer.cpp | 8++++++++
4 files changed, 142 insertions(+), 0 deletions(-)

diff --git a/js/public/Printer.h b/js/public/Printer.h @@ -323,6 +323,28 @@ class JS_PUBLIC_API JSSprinter : public StringPrinter { JSString* release(JSContext* cx) { return releaseJS(cx); } }; +// FixedBufferPrinter, print to a fixed-size buffer. The string in the buffer +// will always be null-terminated after being passed to the constructor. +class FixedBufferPrinter final : public GenericPrinter { + private: + // The first char in the buffer where put will append the next string + char* buffer_; + // The remaining size available in the buffer + size_t size_; + + public: + constexpr FixedBufferPrinter(char* buf, size_t size) + : buffer_(buf), size_(size) { + MOZ_ASSERT(buffer_); + memset(buffer_, 0, size_); + } + + // Puts |len| characters from |s| at the current position. + // If the buffer fills up, this won't do anything. + void put(const char* s, size_t len) override; + using GenericPrinter::put; // pick up |put(const char* s);| +}; + // Fprinter, print a string directly into a file. class JS_PUBLIC_API Fprinter final : public GenericPrinter { private: diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build @@ -108,6 +108,7 @@ UNIFIED_SOURCES += [ "testParserAtom.cpp", "testPersistentRooted.cpp", "testPreserveJitCode.cpp", + "testPrinter.cpp", "testPrintf.cpp", "testPrivateGCThingValue.cpp", "testProfileStrings.cpp", diff --git a/js/src/jsapi-tests/testPrinter.cpp b/js/src/jsapi-tests/testPrinter.cpp @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "js/Printer.h" // FixedBufferPrinter + +#include "jsapi-tests/tests.h" + +using namespace js; + +struct TestBuffer { + const char ASCII_ACK = (char)6; + + char* buffer; + size_t size; + + // len is the buffer size, including terminating null + explicit TestBuffer(const size_t len) : buffer(new char[len + 3]), size(len) { + buffer[size] = ASCII_ACK; // to detect overflow + buffer[size + 1] = ASCII_ACK; + buffer[size + 2] = ASCII_ACK; + } + + ~TestBuffer() { delete[] buffer; } + + // test has written past the end of the buffer + bool hasOverflowed() { + return buffer[size] != ASCII_ACK || buffer[size + 1] != ASCII_ACK || + buffer[size + 2] != ASCII_ACK; + } + + bool matches(const char* expected) { return strcmp(buffer, expected) == 0; } +}; + +BEGIN_TEST(testFixedBufferPrinter) { + // empty buffer + { + TestBuffer actual(0); + FixedBufferPrinter fbp(actual.buffer, 0); + fbp.put("will not fit"); + CHECK(!actual.hasOverflowed()); + } + // buffer is initially null-terminated + { + TestBuffer actual(10); + // make sure the buffer is not null-terminated + memset(actual.buffer, '!', actual.size); + FixedBufferPrinter fbp(actual.buffer, 10); + CHECK(!actual.hasOverflowed()); + CHECK(actual.matches("")); + } + // one put that fits + { + TestBuffer actual(50); + FixedBufferPrinter fbp(actual.buffer, actual.size); + const char* expected = "expected string fits"; + fbp.put(expected); + CHECK(!actual.hasOverflowed()); + CHECK(actual.matches(expected)); + } + // unterminated string in put + { + TestBuffer actual(50); + FixedBufferPrinter fbp(actual.buffer, actual.size); + const char* expected = "okBAD"; + fbp.put(expected, 2); + CHECK(!actual.hasOverflowed()); + CHECK(actual.matches("ok")); + } + // one put that more than fills the buffer + { + TestBuffer actual(16); + FixedBufferPrinter fbp(actual.buffer, actual.size); + const char* expected = "expected string overflow"; + fbp.put(expected); + CHECK(!actual.hasOverflowed()); + CHECK(actual.matches("expected string")); + } + // maintains position over multiple puts that fit + { + TestBuffer actual(16); + FixedBufferPrinter fbp(actual.buffer, actual.size); + fbp.put("expected "); + fbp.put("string"); + CHECK(actual.matches("expected string")); + } + // multiple puts, last one more than fills the buffer + { + TestBuffer actual(9); + FixedBufferPrinter fbp(actual.buffer, actual.size); + fbp.put("expected"); + fbp.put("overflow"); + CHECK(!actual.hasOverflowed()); + CHECK(actual.matches("expected")); + } + // put after buffer is full doesn't overflow + { + TestBuffer actual(2); + FixedBufferPrinter fbp(actual.buffer, actual.size); + fbp.put("exp"); + fbp.put("overflow"); + CHECK(!actual.hasOverflowed()); + CHECK(actual.matches("e")); + } + + return true; +} +END_TEST(testFixedBufferPrinter) diff --git a/js/src/vm/Printer.cpp b/js/src/vm/Printer.cpp @@ -497,6 +497,14 @@ void LSprinter::clear() { hadOOM_ = false; } +void FixedBufferPrinter::put(const char* s, size_t len) { + snprintf(buffer_, size_, "%.*s", int(len), s); + size_t written = std::min(len, size_); + MOZ_ASSERT(size_ - written >= 0); + size_ -= written; + buffer_ += written; +} + void LSprinter::put(const char* s, size_t len) { if (hadOutOfMemory()) { return;