mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 22:28:36 +00:00
f5a7b27f3d
- Bug 1211204 - Remove possible false-negative with D3D9 texture memory reporting. r=mattwoodrow (74adbcad19)
- Bug 1200595 - D3D9 TextureData implementation. r=Bas (2624bd59a3)
- Bug 1200595 - MacIOSurface TextureData implementation. r=mattwoodrow (98130b5d6b)
- Bug 1200595 - EGLImage TextureData implementation. r=mattwoodrow (8b732af2da)
- Bug 1200595 - AndroidSurface TextureData implementation. r=mattwoodrow (79443ff5d7)
- Bug 1200595 - SharedSurface TextureData implementation. r=jgilbert (aadc8c5601)
- Bug 1200595 - DIB TextureData implementation. r=Bas (d7bae178ad)
- Bug 1200595 - Merge TextureClient and ClientTexture back into TextureClient. r=mattwoodrow (890d76ff9b)
- Bug 1200595 - Consolidate the TextureClient's destruction logic. r=mattwoodrow (fa9ac2e414)
- Allow asynchronous D3D11 TextureClients on the main thread. (bug 1217665 part 5, r=nical) (cb77fa88cc)
- Implement the direct bitmap drawing model for plugins. (bug 1217665 part 6, r=mattwoodrow) (82f9015e5a)
- Implement the direct DXGI drawing model for plugins. (bug 1217665 part 7, r=mattwoodrow) (cdeeb52a91)
- Implement the DidComposite NPAPI callback. (bug 1217665 part 8, r=mattwoodrow) (4eb2a790ae)
- Add an NPN_GetValue query to find the browser's DXGI adapter. (bug 1217665 part 10, r=aklotz,mattwoodrow) (84af4e525a)
- Create a D3D11 content device even if D2D is blocked. (bug 1217665 part 11, r=jrmuizel) (142441808e)
- Bug 1229665 - Convert widget clip regions to LayoutDevicePixels. r=botond. (edf746b278)
- Bug 1204715 - Move browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd} to moz.build. r=mshal (8c6b4709bc)
- Bug 1212773 - Pass a base directory to jar maker, instead of a chrome directory. r=gps (1f644706c6)
- Bustage fix for bug 1210687 on a CLOSED TREE. r=me (4cd7000101)
- Bug 1212773 - Extend jar maker syntax for jar file location. r=gps (8119aa2b98)
- Bug 1164039 - moved TelemetryTimestamps.jsm and tests to toolkit/componets/telemetry/. r=dexter (942145656e)
- Bug 1184705 - Search A/B testing cohort identifier should be recorded in FHR, r=rnewman. (b5cf397cf0)
- Bug 1230047 (part 2) - Make several PaintWindow() functions use LayoutDevice coordinates. r=kats. (246e1321ca)
- Bug 1218454 - part 1 - move DialogValueHolder::Get out-of-line; r=bz (2d65438b37)
- Bug 1218454 - part 2 - don't #include nsContentUtils.h from CallbackObject.h; r=bz (59e3b60749)
- Bug 1205945 part.1 Remove unnecessary member of IMEInputHandler, mLastDispatchedCompositionString r=smichaud (2223df8aec)
- Bug 1205945 part.2 Add DispatchCompositionStartEvent() and move the code of OnStartIMEComposition() into it r=smichaud (25449c2bca)
- Bug 1205945 part.3 Move the code of OnUpdateIMEComposition() into DispatchCompositionChangeEvent() r=smichaud (e85a65f2f7)
- Bug 1205945 part.4 Move the code of OnEndIMEComposition() into DispatchCompositionCommitEvent() r=smichaud (21e6218c06)
- Bug 1205945 part.5 Emulate mSelectedRange at dispatching compositionchange or compositioncommit event until OnSelectionChange() is called r=smichaud (f82baaea8e)
- Bug 1205945 part.6 IMEInputHandler::GetAttributedSubstringFromRange() should return stored composition string if the range is in the composition string r=smichaud (53eabe028b)
- Bug 1205399 - Follow-up to fix build bustage for platforms without OS X 10.10 and -Wswitch enabled. r=mstange (c2e8eda9b5)
- Bug 1220337 - Don't show alternate notification actions on OS X 10.8. r=MattN (5ef8449dcc)
- Bug 1224738 - Fix alternate action index getter name on OS X. r=MattN (a6f83ce706)
- Bug 1225908 - AsmSimdTypeToLaneType. r=bbouvier (f850c10bff)
- Bug 1224389 - Odin: refactor types/signatures/values (r=bbouvier) (31bc615781)
- Bug 1224389 - Odin: simplify AsmJSModule global data allocation (r=bbouvier) (3ba518183a)
- Bug 1222684 - IonMonkey: MIPS: Implement callWithPatch and patchCall. r=luke (0ede7a61c3)
- Bug 1224814 - "TraceLogger: Remove redundant checks in BaselineJIT". r=hv1989 (b3865fbdf0)
- Bug 1228369: Rename CodeOffsetLabel into CodeOffset; r=luke (1d0aba3710)
- Bug 1224389 - Odin: refactor stubs, func-ptrs, and masm use (r=bbouvier) (2834725644)
- Bug 1228340: Get rid of the js_ prefix for CodeSpec, CodeName, NumCodeSpecs; r=jorendorff (8ff2b3bcd5)
- Bug 1176214 - Part 2: Preliminary adjustments. r=waldo (f9e2adca8a)
- Bug 1176214 - Part 3: VM core changes. r=waldo (cf5b24fe18)
- Bug 1176214 - Part 4: VM built-in lib changes. r=waldo (603335d2aa)
- Bug 1176214 - Part 5: Ion changes. r=h4writer (b589713e1c)
- Bug 1176214 - Part 6: Odin changes. r=luke (f1dcb025d2)
- Bug 1176214 - Part 7: Ctypes, shell, xpconnect, etc. r=waldo (bdc78e0558)
- Bug 1199578 - test case. r=waldo (6a94fd455a)
- Bug 1211409 - load/store exclusive for ARM-32. r=jolesen (38efc4882e)
- Bug 1205390 - guard against asm.js compilation not being available. r=me (aeefe98cfd)
- Make test runnable on non-Nightly (no bug) r=me (27930210ed)
- Bug 1176214 - Part 8: jit-test changes. r=bbouvier (31c4f42eea)
- Bug 1176214 - Part 9: tests changes. r=bbouvier (a245687f43)
- Bug 1176214 - Part 10: jsapi-tests changes. r=bbouvier (610e0002a2)
- bug 1198656 remove unnecessary reinterpret_casts r=padenot (bc5a67d521)
- bug 1198656 refactor acquiring the content into an object method r=padenot (b7b062fbf1)
- bug 1198656 clear references in mJSChannels on successful content acquire r=padenot (65e5ee1856)
- bug 1198656 delay AudioBuffer allocation until required r=padenot (57d0fd0d01)
- bug 1199559 remove now unused SetRawChannelContents r=padenot (40685ef783)
- Bug 1203616 - Properly scale the input buffer of a WaveShaperNode before processing it with the curve. r=karlt (12e9592a9e)
- Bug 1186343: Throw an InvalidStateError when we set the curve attribute of a WaveShaperNode with a Float32Array of length less than 2; r=padenot,smaug (df83b21fd0)
- bug 1188244 throw in SetCurve() on OOM r=padenot (17967b7b58)
- Bug 1176214 - Part 11: Changes to DOM, except for WebGL. r=bz, r=clb (c40e5c2a68)
- Bug 1176214 - Part 12: Changes to WebGL. r=bz, r=clb (228c90da3c)
- Bug 1176214 - Part 13: Changes to ipc. r=mrbkap (4b11d4e509)
- Bug 1176214 - Part 14: Changes to netwerk. r=jduell (91568c8444)
- Bug 1176214 - Part 15: Changes to xpcom. r=nfroyd (df8d080070)
- Revert "Bug 1176214 - Part 14: Changes to netwerk. r=jduell" (d0aedbac94)
- improved backport of PM because of newer JS_GetArrayBufferData (9d8188ff5e)
- bug 1199559 write decodeAudioData buffer in a format suitable for direct use by AudioBuffer r=padenot (da00bab1a1)
- bug 1225003 null-check mBuffer in SizeOfExcludingThis() r=padenot (c01d389f10)
- Bug 1225365 - Fix assertion in the nsScriptNameSpaceManager memory reporter. r=bz. (a5605a8923)
- Bug 1229458 - Remove SizeOfIncludingThisMustBeUnshared() from string classes. r=mccr8. (26abcea276)
- Bug 1214506. Ensure OggReader sets proper IDs for its tracks. r=jya (a270b02301)
- bits of Bug 1188812 - Obtain CDM can render capability and store into MediaInfo (321388180e)
- Bug 1226450 - Report audio/video codecs used in HTMLMediaElement and WebAudio via telemetry. r=jya (e722b409b7)
1056 lines
27 KiB
C++
1056 lines
27 KiB
C++
/* -*- 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 "mozilla/CheckedInt.h"
|
|
#include "mozilla/double-conversion.h"
|
|
#include "mozilla/MemoryReporting.h"
|
|
|
|
using double_conversion::DoubleToStringConverter;
|
|
|
|
#ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
|
|
nsTSubstring_CharT::nsTSubstring_CharT(char_type* aData, size_type aLength,
|
|
uint32_t aFlags)
|
|
: mData(aData),
|
|
mLength(aLength),
|
|
mFlags(aFlags)
|
|
{
|
|
if (aFlags & F_OWNED) {
|
|
STRING_STAT_INCREMENT(Adopt);
|
|
MOZ_LOG_CTOR(mData, "StringAdopt", 1);
|
|
}
|
|
}
|
|
#endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
|
|
|
|
/**
|
|
* helper function for down-casting a nsTSubstring to a nsTFixedString.
|
|
*/
|
|
inline const nsTFixedString_CharT*
|
|
AsFixedString(const nsTSubstring_CharT* aStr)
|
|
{
|
|
return static_cast<const nsTFixedString_CharT*>(aStr);
|
|
}
|
|
|
|
|
|
/**
|
|
* this function is called to prepare mData for writing. the given capacity
|
|
* indicates the required minimum storage size for mData, in sizeof(char_type)
|
|
* increments. this function returns true if the operation succeeds. it also
|
|
* returns the old data and old flags members if mData is newly allocated.
|
|
* the old data must be released by the caller.
|
|
*/
|
|
bool
|
|
nsTSubstring_CharT::MutatePrep(size_type aCapacity, char_type** aOldData,
|
|
uint32_t* aOldFlags)
|
|
{
|
|
// initialize to no old data
|
|
*aOldData = nullptr;
|
|
*aOldFlags = 0;
|
|
|
|
size_type curCapacity = Capacity();
|
|
|
|
// If |aCapacity > kMaxCapacity|, then our doubling algorithm may not be
|
|
// able to allocate it. Just bail out in cases like that. We don't want
|
|
// to be allocating 2GB+ strings anyway.
|
|
static_assert((sizeof(nsStringBuffer) & 0x1) == 0,
|
|
"bad size for nsStringBuffer");
|
|
const size_type kMaxCapacity =
|
|
(size_type(-1) / 2 - sizeof(nsStringBuffer)) / sizeof(char_type) - 2;
|
|
if (aCapacity > kMaxCapacity) {
|
|
// Also assert for |aCapacity| equal to |size_type(-1)|, since we used to
|
|
// use that value to flag immutability.
|
|
NS_ASSERTION(aCapacity != size_type(-1), "Bogus capacity");
|
|
return false;
|
|
}
|
|
|
|
// |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
|
|
// need to allocate a new buffer. We cannot use the existing buffer even
|
|
// though it might be large enough.
|
|
|
|
if (curCapacity != 0) {
|
|
if (aCapacity <= curCapacity) {
|
|
mFlags &= ~F_VOIDED; // mutation clears voided flag
|
|
return true;
|
|
}
|
|
|
|
// Use doubling algorithm when forced to increase available capacity.
|
|
size_type temp = curCapacity;
|
|
while (temp < aCapacity) {
|
|
temp <<= 1;
|
|
}
|
|
NS_ASSERTION(XPCOM_MIN(temp, kMaxCapacity) >= aCapacity,
|
|
"should have hit the early return at the top");
|
|
aCapacity = XPCOM_MIN(temp, kMaxCapacity);
|
|
}
|
|
|
|
//
|
|
// several cases:
|
|
//
|
|
// (1) we have a shared buffer (mFlags & F_SHARED)
|
|
// (2) we have an owned buffer (mFlags & F_OWNED)
|
|
// (3) we have a fixed buffer (mFlags & F_FIXED)
|
|
// (4) we have a readonly buffer
|
|
//
|
|
// requiring that we in some cases preserve the data before creating
|
|
// a new buffer complicates things just a bit ;-)
|
|
//
|
|
|
|
size_type storageSize = (aCapacity + 1) * sizeof(char_type);
|
|
|
|
// case #1
|
|
if (mFlags & F_SHARED) {
|
|
nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
|
|
if (!hdr->IsReadonly()) {
|
|
nsStringBuffer* newHdr = nsStringBuffer::Realloc(hdr, storageSize);
|
|
if (!newHdr) {
|
|
return false; // out-of-memory (original header left intact)
|
|
}
|
|
|
|
hdr = newHdr;
|
|
mData = (char_type*)hdr->Data();
|
|
mFlags &= ~F_VOIDED; // mutation clears voided flag
|
|
return true;
|
|
}
|
|
}
|
|
|
|
char_type* newData;
|
|
uint32_t newDataFlags;
|
|
|
|
// if we have a fixed buffer of sufficient size, then use it. this helps
|
|
// avoid heap allocations.
|
|
if ((mFlags & F_CLASS_FIXED) &&
|
|
(aCapacity < AsFixedString(this)->mFixedCapacity)) {
|
|
newData = AsFixedString(this)->mFixedBuf;
|
|
newDataFlags = F_TERMINATED | F_FIXED;
|
|
} else {
|
|
// if we reach here then, we must allocate a new buffer. we cannot
|
|
// make use of our F_OWNED or F_FIXED buffers because they are not
|
|
// large enough.
|
|
|
|
nsStringBuffer* newHdr =
|
|
nsStringBuffer::Alloc(storageSize).take();
|
|
if (!newHdr) {
|
|
return false; // we are still in a consistent state
|
|
}
|
|
|
|
newData = (char_type*)newHdr->Data();
|
|
newDataFlags = F_TERMINATED | F_SHARED;
|
|
}
|
|
|
|
// save old data and flags
|
|
*aOldData = mData;
|
|
*aOldFlags = mFlags;
|
|
|
|
mData = newData;
|
|
SetDataFlags(newDataFlags);
|
|
|
|
// mLength does not change
|
|
|
|
// though we are not necessarily terminated at the moment, now is probably
|
|
// still the best time to set F_TERMINATED.
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::Finalize()
|
|
{
|
|
::ReleaseData(mData, mFlags);
|
|
// mData, mLength, and mFlags are purposefully left dangling
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::ReplacePrep(index_type aCutStart,
|
|
size_type aCutLength,
|
|
size_type aNewLength)
|
|
{
|
|
aCutLength = XPCOM_MIN(aCutLength, mLength - aCutStart);
|
|
|
|
mozilla::CheckedInt<size_type> newTotalLen = mLength;
|
|
newTotalLen += aNewLength;
|
|
newTotalLen -= aCutLength;
|
|
if (!newTotalLen.isValid()) {
|
|
return false;
|
|
}
|
|
|
|
if (aCutStart == mLength && Capacity() > newTotalLen.value()) {
|
|
mFlags &= ~F_VOIDED;
|
|
mData[newTotalLen.value()] = char_type(0);
|
|
mLength = newTotalLen.value();
|
|
return true;
|
|
}
|
|
|
|
return ReplacePrepInternal(aCutStart, aCutLength, aNewLength,
|
|
newTotalLen.value());
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::ReplacePrepInternal(index_type aCutStart, size_type aCutLen,
|
|
size_type aFragLen, size_type aNewLen)
|
|
{
|
|
char_type* oldData;
|
|
uint32_t oldFlags;
|
|
if (!MutatePrep(aNewLen, &oldData, &oldFlags)) {
|
|
return false; // out-of-memory
|
|
}
|
|
|
|
if (oldData) {
|
|
// determine whether or not we need to copy part of the old string
|
|
// over to the new string.
|
|
|
|
if (aCutStart > 0) {
|
|
// copy prefix from old string
|
|
char_traits::copy(mData, oldData, aCutStart);
|
|
}
|
|
|
|
if (aCutStart + aCutLen < mLength) {
|
|
// copy suffix from old string to new offset
|
|
size_type from = aCutStart + aCutLen;
|
|
size_type fromLen = mLength - from;
|
|
uint32_t to = aCutStart + aFragLen;
|
|
char_traits::copy(mData + to, oldData + from, fromLen);
|
|
}
|
|
|
|
::ReleaseData(oldData, oldFlags);
|
|
} else {
|
|
// original data remains intact
|
|
|
|
// determine whether or not we need to move part of the existing string
|
|
// to make room for the requested hole.
|
|
if (aFragLen != aCutLen && aCutStart + aCutLen < mLength) {
|
|
uint32_t from = aCutStart + aCutLen;
|
|
uint32_t fromLen = mLength - from;
|
|
uint32_t to = aCutStart + aFragLen;
|
|
char_traits::move(mData + to, mData + from, fromLen);
|
|
}
|
|
}
|
|
|
|
// add null terminator (mutable mData always has room for the null-
|
|
// terminator).
|
|
mData[aNewLen] = char_type(0);
|
|
mLength = aNewLen;
|
|
|
|
return true;
|
|
}
|
|
|
|
nsTSubstring_CharT::size_type
|
|
nsTSubstring_CharT::Capacity() const
|
|
{
|
|
// return 0 to indicate an immutable or 0-sized buffer
|
|
|
|
size_type capacity;
|
|
if (mFlags & F_SHARED) {
|
|
// if the string is readonly, then we pretend that it has no capacity.
|
|
nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
|
|
if (hdr->IsReadonly()) {
|
|
capacity = 0;
|
|
} else {
|
|
capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
|
|
}
|
|
} else if (mFlags & F_FIXED) {
|
|
capacity = AsFixedString(this)->mFixedCapacity;
|
|
} else if (mFlags & F_OWNED) {
|
|
// we don't store the capacity of an adopted buffer because that would
|
|
// require an additional member field. the best we can do is base the
|
|
// capacity on our length. remains to be seen if this is the right
|
|
// trade-off.
|
|
capacity = mLength;
|
|
} else {
|
|
capacity = 0;
|
|
}
|
|
|
|
return capacity;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::EnsureMutable(size_type aNewLen)
|
|
{
|
|
if (aNewLen == size_type(-1) || aNewLen == mLength) {
|
|
if (mFlags & (F_FIXED | F_OWNED)) {
|
|
return true;
|
|
}
|
|
if ((mFlags & F_SHARED) &&
|
|
!nsStringBuffer::FromData(mData)->IsReadonly()) {
|
|
return true;
|
|
}
|
|
|
|
aNewLen = mLength;
|
|
}
|
|
return SetLength(aNewLen, mozilla::fallible);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// This version of Assign is optimized for single-character assignment.
|
|
void
|
|
nsTSubstring_CharT::Assign(char_type aChar)
|
|
{
|
|
if (!ReplacePrep(0, mLength, 1)) {
|
|
AllocFailed(mLength);
|
|
}
|
|
|
|
*mData = aChar;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Assign(char_type aChar, const fallible_t&)
|
|
{
|
|
if (!ReplacePrep(0, mLength, 1)) {
|
|
return false;
|
|
}
|
|
|
|
*mData = aChar;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::Assign(const char_type* aData)
|
|
{
|
|
if (!Assign(aData, size_type(-1), mozilla::fallible)) {
|
|
AllocFailed(char_traits::length(aData));
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength)
|
|
{
|
|
if (!Assign(aData, aLength, mozilla::fallible)) {
|
|
AllocFailed(aLength == size_type(-1) ? char_traits::length(aData)
|
|
: aLength);
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength,
|
|
const fallible_t& aFallible)
|
|
{
|
|
if (!aData || aLength == 0) {
|
|
Truncate();
|
|
return true;
|
|
}
|
|
|
|
if (aLength == size_type(-1)) {
|
|
aLength = char_traits::length(aData);
|
|
}
|
|
|
|
if (IsDependentOn(aData, aData + aLength)) {
|
|
return Assign(string_type(aData, aLength), aFallible);
|
|
}
|
|
|
|
if (!ReplacePrep(0, mLength, aLength)) {
|
|
return false;
|
|
}
|
|
|
|
char_traits::copy(mData, aData, aLength);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength)
|
|
{
|
|
if (!AssignASCII(aData, aLength, mozilla::fallible)) {
|
|
AllocFailed(aLength);
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength,
|
|
const fallible_t& aFallible)
|
|
{
|
|
// A Unicode string can't depend on an ASCII string buffer,
|
|
// so this dependence check only applies to CStrings.
|
|
#ifdef CharT_is_char
|
|
if (IsDependentOn(aData, aData + aLength)) {
|
|
return Assign(string_type(aData, aLength), aFallible);
|
|
}
|
|
#endif
|
|
|
|
if (!ReplacePrep(0, mLength, aLength)) {
|
|
return false;
|
|
}
|
|
|
|
char_traits::copyASCII(mData, aData, aLength);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::AssignLiteral(const char_type* aData, size_type aLength)
|
|
{
|
|
::ReleaseData(mData, mFlags);
|
|
mData = const_cast<char_type*>(aData);
|
|
mLength = aLength;
|
|
SetDataFlags(F_TERMINATED | F_LITERAL);
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::Assign(const self_type& aStr)
|
|
{
|
|
if (!Assign(aStr, mozilla::fallible)) {
|
|
AllocFailed(aStr.Length());
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Assign(const self_type& aStr, const fallible_t& aFallible)
|
|
{
|
|
// |aStr| could be sharable. We need to check its flags to know how to
|
|
// deal with it.
|
|
|
|
if (&aStr == this) {
|
|
return true;
|
|
}
|
|
|
|
if (!aStr.mLength) {
|
|
Truncate();
|
|
mFlags |= aStr.mFlags & F_VOIDED;
|
|
return true;
|
|
}
|
|
|
|
if (aStr.mFlags & F_SHARED) {
|
|
// nice! we can avoid a string copy :-)
|
|
|
|
// |aStr| should be null-terminated
|
|
NS_ASSERTION(aStr.mFlags & F_TERMINATED, "shared, but not terminated");
|
|
|
|
::ReleaseData(mData, mFlags);
|
|
|
|
mData = aStr.mData;
|
|
mLength = aStr.mLength;
|
|
SetDataFlags(F_TERMINATED | F_SHARED);
|
|
|
|
// get an owning reference to the mData
|
|
nsStringBuffer::FromData(mData)->AddRef();
|
|
return true;
|
|
} else if (aStr.mFlags & F_LITERAL) {
|
|
MOZ_ASSERT(aStr.mFlags & F_TERMINATED, "Unterminated literal");
|
|
|
|
AssignLiteral(aStr.mData, aStr.mLength);
|
|
return true;
|
|
}
|
|
|
|
// else, treat this like an ordinary assignment.
|
|
return Assign(aStr.Data(), aStr.Length(), aFallible);
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple)
|
|
{
|
|
if (!Assign(aTuple, mozilla::fallible)) {
|
|
AllocFailed(aTuple.Length());
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple,
|
|
const fallible_t& aFallible)
|
|
{
|
|
if (aTuple.IsDependentOn(mData, mData + mLength)) {
|
|
// take advantage of sharing here...
|
|
return Assign(string_type(aTuple), aFallible);
|
|
}
|
|
|
|
size_type length = aTuple.Length();
|
|
|
|
// don't use ReplacePrep here because it changes the length
|
|
char_type* oldData;
|
|
uint32_t oldFlags;
|
|
if (!MutatePrep(length, &oldData, &oldFlags)) {
|
|
return false;
|
|
}
|
|
|
|
if (oldData) {
|
|
::ReleaseData(oldData, oldFlags);
|
|
}
|
|
|
|
aTuple.WriteTo(mData, length);
|
|
mData[length] = 0;
|
|
mLength = length;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::Adopt(char_type* aData, size_type aLength)
|
|
{
|
|
if (aData) {
|
|
::ReleaseData(mData, mFlags);
|
|
|
|
if (aLength == size_type(-1)) {
|
|
aLength = char_traits::length(aData);
|
|
}
|
|
|
|
mData = aData;
|
|
mLength = aLength;
|
|
SetDataFlags(F_TERMINATED | F_OWNED);
|
|
|
|
STRING_STAT_INCREMENT(Adopt);
|
|
// Treat this as construction of a "StringAdopt" object for leak
|
|
// tracking purposes.
|
|
MOZ_LOG_CTOR(mData, "StringAdopt", 1);
|
|
} else {
|
|
SetIsVoid(true);
|
|
}
|
|
}
|
|
|
|
|
|
// This version of Replace is optimized for single-character replacement.
|
|
void
|
|
nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
|
|
char_type aChar)
|
|
{
|
|
aCutStart = XPCOM_MIN(aCutStart, Length());
|
|
|
|
if (ReplacePrep(aCutStart, aCutLength, 1)) {
|
|
mData[aCutStart] = aChar;
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
|
|
char_type aChar,
|
|
const fallible_t&)
|
|
{
|
|
aCutStart = XPCOM_MIN(aCutStart, Length());
|
|
|
|
if (!ReplacePrep(aCutStart, aCutLength, 1)) {
|
|
return false;
|
|
}
|
|
|
|
mData[aCutStart] = aChar;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
|
|
const char_type* aData, size_type aLength)
|
|
{
|
|
if (!Replace(aCutStart, aCutLength, aData, aLength,
|
|
mozilla::fallible)) {
|
|
AllocFailed(Length() - aCutLength + 1);
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
|
|
const char_type* aData, size_type aLength,
|
|
const fallible_t& aFallible)
|
|
{
|
|
// unfortunately, some callers pass null :-(
|
|
if (!aData) {
|
|
aLength = 0;
|
|
} else {
|
|
if (aLength == size_type(-1)) {
|
|
aLength = char_traits::length(aData);
|
|
}
|
|
|
|
if (IsDependentOn(aData, aData + aLength)) {
|
|
nsTAutoString_CharT temp(aData, aLength);
|
|
return Replace(aCutStart, aCutLength, temp, aFallible);
|
|
}
|
|
}
|
|
|
|
aCutStart = XPCOM_MIN(aCutStart, Length());
|
|
|
|
bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
if (aLength > 0) {
|
|
char_traits::copy(mData + aCutStart, aData, aLength);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
|
|
const char* aData, size_type aLength)
|
|
{
|
|
if (!ReplaceASCII(aCutStart, aCutLength, aData, aLength, mozilla::fallible)) {
|
|
AllocFailed(Length() - aCutLength + 1);
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
|
|
const char* aData, size_type aLength,
|
|
const fallible_t& aFallible)
|
|
{
|
|
if (aLength == size_type(-1)) {
|
|
aLength = strlen(aData);
|
|
}
|
|
|
|
// A Unicode string can't depend on an ASCII string buffer,
|
|
// so this dependence check only applies to CStrings.
|
|
#ifdef CharT_is_char
|
|
if (IsDependentOn(aData, aData + aLength)) {
|
|
nsTAutoString_CharT temp(aData, aLength);
|
|
return Replace(aCutStart, aCutLength, temp, aFallible);
|
|
}
|
|
#endif
|
|
|
|
aCutStart = XPCOM_MIN(aCutStart, Length());
|
|
|
|
bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
if (aLength > 0) {
|
|
char_traits::copyASCII(mData + aCutStart, aData, aLength);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
|
|
const substring_tuple_type& aTuple)
|
|
{
|
|
if (aTuple.IsDependentOn(mData, mData + mLength)) {
|
|
nsTAutoString_CharT temp(aTuple);
|
|
Replace(aCutStart, aCutLength, temp);
|
|
return;
|
|
}
|
|
|
|
size_type length = aTuple.Length();
|
|
|
|
aCutStart = XPCOM_MIN(aCutStart, Length());
|
|
|
|
if (ReplacePrep(aCutStart, aCutLength, length) && length > 0) {
|
|
aTuple.WriteTo(mData + aCutStart, length);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::ReplaceLiteral(index_type aCutStart, size_type aCutLength,
|
|
const char_type* aData, size_type aLength)
|
|
{
|
|
aCutStart = XPCOM_MIN(aCutStart, Length());
|
|
|
|
if (!aCutStart && aCutLength == Length()) {
|
|
AssignLiteral(aData, aLength);
|
|
} else if (ReplacePrep(aCutStart, aCutLength, aLength) && aLength > 0) {
|
|
char_traits::copy(mData + aCutStart, aData, aLength);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::SetCapacity(size_type aCapacity)
|
|
{
|
|
if (!SetCapacity(aCapacity, mozilla::fallible)) {
|
|
AllocFailed(aCapacity);
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::SetCapacity(size_type aCapacity, const fallible_t&)
|
|
{
|
|
// capacity does not include room for the terminating null char
|
|
|
|
// if our capacity is reduced to zero, then free our buffer.
|
|
if (aCapacity == 0) {
|
|
::ReleaseData(mData, mFlags);
|
|
mData = char_traits::sEmptyBuffer;
|
|
mLength = 0;
|
|
SetDataFlags(F_TERMINATED);
|
|
return true;
|
|
}
|
|
|
|
char_type* oldData;
|
|
uint32_t oldFlags;
|
|
if (!MutatePrep(aCapacity, &oldData, &oldFlags)) {
|
|
return false; // out-of-memory
|
|
}
|
|
|
|
// compute new string length
|
|
size_type newLen = XPCOM_MIN(mLength, aCapacity);
|
|
|
|
if (oldData) {
|
|
// preserve old data
|
|
if (mLength > 0) {
|
|
char_traits::copy(mData, oldData, newLen);
|
|
}
|
|
|
|
::ReleaseData(oldData, oldFlags);
|
|
}
|
|
|
|
// adjust mLength if our buffer shrunk down in size
|
|
if (newLen < mLength) {
|
|
mLength = newLen;
|
|
}
|
|
|
|
// always null-terminate here, even if the buffer got longer. this is
|
|
// for backwards compat with the old string implementation.
|
|
mData[aCapacity] = char_type(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::SetLength(size_type aLength)
|
|
{
|
|
SetCapacity(aLength);
|
|
mLength = aLength;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::SetLength(size_type aLength, const fallible_t& aFallible)
|
|
{
|
|
if (!SetCapacity(aLength, aFallible)) {
|
|
return false;
|
|
}
|
|
|
|
mLength = aLength;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::SetIsVoid(bool aVal)
|
|
{
|
|
if (aVal) {
|
|
Truncate();
|
|
mFlags |= F_VOIDED;
|
|
} else {
|
|
mFlags &= ~F_VOIDED;
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Equals(const self_type& aStr) const
|
|
{
|
|
return mLength == aStr.mLength &&
|
|
char_traits::compare(mData, aStr.mData, mLength) == 0;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Equals(const self_type& aStr,
|
|
const comparator_type& aComp) const
|
|
{
|
|
return mLength == aStr.mLength &&
|
|
aComp(mData, aStr.mData, mLength, aStr.mLength) == 0;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Equals(const char_type* aData) const
|
|
{
|
|
// unfortunately, some callers pass null :-(
|
|
if (!aData) {
|
|
NS_NOTREACHED("null data pointer");
|
|
return mLength == 0;
|
|
}
|
|
|
|
// XXX avoid length calculation?
|
|
size_type length = char_traits::length(aData);
|
|
return mLength == length &&
|
|
char_traits::compare(mData, aData, mLength) == 0;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::Equals(const char_type* aData,
|
|
const comparator_type& aComp) const
|
|
{
|
|
// unfortunately, some callers pass null :-(
|
|
if (!aData) {
|
|
NS_NOTREACHED("null data pointer");
|
|
return mLength == 0;
|
|
}
|
|
|
|
// XXX avoid length calculation?
|
|
size_type length = char_traits::length(aData);
|
|
return mLength == length && aComp(mData, aData, mLength, length) == 0;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::EqualsASCII(const char* aData, size_type aLen) const
|
|
{
|
|
return mLength == aLen &&
|
|
char_traits::compareASCII(mData, aData, aLen) == 0;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::EqualsASCII(const char* aData) const
|
|
{
|
|
return char_traits::compareASCIINullTerminated(mData, mLength, aData) == 0;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::LowerCaseEqualsASCII(const char* aData,
|
|
size_type aLen) const
|
|
{
|
|
return mLength == aLen &&
|
|
char_traits::compareLowerCaseToASCII(mData, aData, aLen) == 0;
|
|
}
|
|
|
|
bool
|
|
nsTSubstring_CharT::LowerCaseEqualsASCII(const char* aData) const
|
|
{
|
|
return char_traits::compareLowerCaseToASCIINullTerminated(mData,
|
|
mLength,
|
|
aData) == 0;
|
|
}
|
|
|
|
nsTSubstring_CharT::size_type
|
|
nsTSubstring_CharT::CountChar(char_type aChar) const
|
|
{
|
|
const char_type* start = mData;
|
|
const char_type* end = mData + mLength;
|
|
|
|
return NS_COUNT(start, end, aChar);
|
|
}
|
|
|
|
int32_t
|
|
nsTSubstring_CharT::FindChar(char_type aChar, index_type aOffset) const
|
|
{
|
|
if (aOffset < mLength) {
|
|
const char_type* result = char_traits::find(mData + aOffset,
|
|
mLength - aOffset, aChar);
|
|
if (result) {
|
|
return result - mData;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::StripChar(char_type aChar, int32_t aOffset)
|
|
{
|
|
if (mLength == 0 || aOffset >= int32_t(mLength)) {
|
|
return;
|
|
}
|
|
|
|
if (!EnsureMutable()) { // XXX do this lazily?
|
|
AllocFailed(mLength);
|
|
}
|
|
|
|
// XXX(darin): this code should defer writing until necessary.
|
|
|
|
char_type* to = mData + aOffset;
|
|
char_type* from = mData + aOffset;
|
|
char_type* end = mData + mLength;
|
|
|
|
while (from < end) {
|
|
char_type theChar = *from++;
|
|
if (aChar != theChar) {
|
|
*to++ = theChar;
|
|
}
|
|
}
|
|
*to = char_type(0); // add the null
|
|
mLength = to - mData;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::StripChars(const char_type* aChars, uint32_t aOffset)
|
|
{
|
|
if (aOffset >= uint32_t(mLength)) {
|
|
return;
|
|
}
|
|
|
|
if (!EnsureMutable()) { // XXX do this lazily?
|
|
AllocFailed(mLength);
|
|
}
|
|
|
|
// XXX(darin): this code should defer writing until necessary.
|
|
|
|
char_type* to = mData + aOffset;
|
|
char_type* from = mData + aOffset;
|
|
char_type* end = mData + mLength;
|
|
|
|
while (from < end) {
|
|
char_type theChar = *from++;
|
|
const char_type* test = aChars;
|
|
|
|
for (; *test && *test != theChar; ++test);
|
|
|
|
if (!*test) {
|
|
// Not stripped, copy this char.
|
|
*to++ = theChar;
|
|
}
|
|
}
|
|
*to = char_type(0); // add the null
|
|
mLength = to - mData;
|
|
}
|
|
|
|
int
|
|
nsTSubstring_CharT::AppendFunc(void* aArg, const char* aStr, uint32_t aLen)
|
|
{
|
|
self_type* self = static_cast<self_type*>(aArg);
|
|
|
|
// NSPR sends us the final null terminator even though we don't want it
|
|
if (aLen && aStr[aLen - 1] == '\0') {
|
|
--aLen;
|
|
}
|
|
|
|
self->AppendASCII(aStr, aLen);
|
|
|
|
return aLen;
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::AppendPrintf(const char* aFormat, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, aFormat);
|
|
uint32_t r = PR_vsxprintf(AppendFunc, this, aFormat, ap);
|
|
if (r == (uint32_t)-1) {
|
|
NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
|
|
}
|
|
va_end(ap);
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::AppendPrintf(const char* aFormat, va_list aAp)
|
|
{
|
|
uint32_t r = PR_vsxprintf(AppendFunc, this, aFormat, aAp);
|
|
if (r == (uint32_t)-1) {
|
|
NS_RUNTIMEABORT("Allocation or other failure in PR_vsxprintf");
|
|
}
|
|
}
|
|
|
|
/* hack to make sure we define FormatWithoutTrailingZeros only once */
|
|
#ifdef CharT_is_PRUnichar
|
|
// Returns the length of the formatted aDouble in aBuf.
|
|
static int
|
|
FormatWithoutTrailingZeros(char (&aBuf)[40], double aDouble,
|
|
int aPrecision)
|
|
{
|
|
static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO |
|
|
DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
|
|
"Infinity",
|
|
"NaN",
|
|
'e',
|
|
-6, 21,
|
|
6, 1);
|
|
double_conversion::StringBuilder builder(aBuf, sizeof(aBuf));
|
|
bool exponential_notation = false;
|
|
converter.ToPrecision(aDouble, aPrecision, &exponential_notation, &builder);
|
|
int length = builder.position();
|
|
char* formattedDouble = builder.Finalize();
|
|
|
|
// If we have a shorter string than aPrecision, it means we have a special
|
|
// value (NaN or Infinity). All other numbers will be formatted with at
|
|
// least aPrecision digits.
|
|
if (length <= aPrecision) {
|
|
return length;
|
|
}
|
|
|
|
char* end = formattedDouble + length;
|
|
char* decimalPoint = strchr(aBuf, '.');
|
|
// No trailing zeros to remove.
|
|
if (!decimalPoint) {
|
|
return length;
|
|
}
|
|
|
|
if (MOZ_UNLIKELY(exponential_notation)) {
|
|
// We need to check for cases like 1.00000e-10 (yes, this is
|
|
// disgusting).
|
|
char* exponent = end - 1;
|
|
for (; ; --exponent) {
|
|
if (*exponent == 'e') {
|
|
break;
|
|
}
|
|
}
|
|
char* zerosBeforeExponent = exponent - 1;
|
|
for (; zerosBeforeExponent != decimalPoint; --zerosBeforeExponent) {
|
|
if (*zerosBeforeExponent != '0') {
|
|
break;
|
|
}
|
|
}
|
|
if (zerosBeforeExponent == decimalPoint) {
|
|
--zerosBeforeExponent;
|
|
}
|
|
// Slide the exponent to the left over the trailing zeros. Don't
|
|
// worry about copying the trailing NUL character.
|
|
size_t exponentSize = end - exponent;
|
|
memmove(zerosBeforeExponent + 1, exponent, exponentSize);
|
|
length -= exponent - (zerosBeforeExponent + 1);
|
|
} else {
|
|
char* trailingZeros = end - 1;
|
|
for (; trailingZeros != decimalPoint; --trailingZeros) {
|
|
if (*trailingZeros != '0') {
|
|
break;
|
|
}
|
|
}
|
|
if (trailingZeros == decimalPoint) {
|
|
--trailingZeros;
|
|
}
|
|
length -= end - (trailingZeros + 1);
|
|
}
|
|
|
|
return length;
|
|
}
|
|
#endif /* CharT_is_PRUnichar */
|
|
|
|
void
|
|
nsTSubstring_CharT::AppendFloat(float aFloat)
|
|
{
|
|
char buf[40];
|
|
int length = FormatWithoutTrailingZeros(buf, aFloat, 6);
|
|
AppendASCII(buf, length);
|
|
}
|
|
|
|
void
|
|
nsTSubstring_CharT::AppendFloat(double aFloat)
|
|
{
|
|
char buf[40];
|
|
int length = FormatWithoutTrailingZeros(buf, aFloat, 15);
|
|
AppendASCII(buf, length);
|
|
}
|
|
|
|
size_t
|
|
nsTSubstring_CharT::SizeOfExcludingThisIfUnshared(
|
|
mozilla::MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
if (mFlags & F_SHARED) {
|
|
return nsStringBuffer::FromData(mData)->
|
|
SizeOfIncludingThisIfUnshared(aMallocSizeOf);
|
|
}
|
|
if (mFlags & F_OWNED) {
|
|
return aMallocSizeOf(mData);
|
|
}
|
|
|
|
// If we reach here, exactly one of the following must be true:
|
|
// - F_VOIDED is set, and mData points to sEmptyBuffer;
|
|
// - F_FIXED is set, and mData points to a buffer within a string
|
|
// object (e.g. nsAutoString);
|
|
// - None of F_SHARED, F_OWNED, F_FIXED is set, and mData points to a buffer
|
|
// owned by something else.
|
|
//
|
|
// In all three cases, we don't measure it.
|
|
return 0;
|
|
}
|
|
|
|
size_t
|
|
nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared(
|
|
mozilla::MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
// This is identical to SizeOfExcludingThisIfUnshared except for the
|
|
// F_SHARED case.
|
|
if (mFlags & F_SHARED) {
|
|
return nsStringBuffer::FromData(mData)->
|
|
SizeOfIncludingThisEvenIfShared(aMallocSizeOf);
|
|
}
|
|
if (mFlags & F_OWNED) {
|
|
return aMallocSizeOf(mData);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
size_t
|
|
nsTSubstring_CharT::SizeOfIncludingThisIfUnshared(
|
|
mozilla::MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
return aMallocSizeOf(this) + SizeOfExcludingThisIfUnshared(aMallocSizeOf);
|
|
}
|
|
|
|
size_t
|
|
nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared(
|
|
mozilla::MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
return aMallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
|
|
}
|
|
|