1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsStringBuffer.h"
9 #include "mozilla/MemoryReporting.h"
10 #include "nsISupportsImpl.h"
14 # include "nsStringStats.h"
16 # define STRING_STAT_INCREMENT(_s)
19 void nsStringBuffer::AddRef() {
20 // Memory synchronization is not required when incrementing a
21 // reference count. The first increment of a reference count on a
22 // thread is not important, since the first use of the object on a
23 // thread can happen before it. What is important is the transfer
24 // of the pointer to that thread, which may happen prior to the
25 // first increment on that thread. The necessary memory
26 // synchronization is done by the mechanism that transfers the
27 // pointer between threads.
28 #ifdef NS_BUILD_REFCNT_LOGGING
31 mRefCount
.fetch_add(1, std::memory_order_relaxed
)
32 #ifdef NS_BUILD_REFCNT_LOGGING
36 STRING_STAT_INCREMENT(Share
);
37 NS_LOG_ADDREF(this, count
, "nsStringBuffer", sizeof(*this));
40 void nsStringBuffer::Release() {
41 // Since this may be the last release on this thread, we need
42 // release semantics so that prior writes on this thread are visible
43 // to the thread that destroys the object when it reads mValue with
45 uint32_t count
= mRefCount
.fetch_sub(1, std::memory_order_release
) - 1;
46 NS_LOG_RELEASE(this, count
, "nsStringBuffer");
48 // We're going to destroy the object on this thread, so we need
49 // acquire semantics to synchronize with the memory released by
50 // the last release on other threads, that is, to ensure that
51 // writes prior to that release are now visible on this thread.
52 count
= mRefCount
.load(std::memory_order_acquire
);
54 STRING_STAT_INCREMENT(Free
);
55 free(this); // we were allocated with |malloc|
60 * Alloc returns a pointer to a new string header with set capacity.
62 already_AddRefed
<nsStringBuffer
> nsStringBuffer::Alloc(size_t aSize
) {
63 NS_ASSERTION(aSize
!= 0, "zero capacity allocation not allowed");
64 NS_ASSERTION(sizeof(nsStringBuffer
) + aSize
<= size_t(uint32_t(-1)) &&
65 sizeof(nsStringBuffer
) + aSize
> aSize
,
66 "mStorageSize will truncate");
68 nsStringBuffer
* hdr
= (nsStringBuffer
*)malloc(sizeof(nsStringBuffer
) + aSize
);
70 STRING_STAT_INCREMENT(Alloc
);
73 hdr
->mStorageSize
= aSize
;
74 NS_LOG_ADDREF(hdr
, 1, "nsStringBuffer", sizeof(*hdr
));
76 return already_AddRefed(hdr
);
79 nsStringBuffer
* nsStringBuffer::Realloc(nsStringBuffer
* aHdr
, size_t aSize
) {
80 STRING_STAT_INCREMENT(Realloc
);
82 NS_ASSERTION(aSize
!= 0, "zero capacity allocation not allowed");
83 NS_ASSERTION(sizeof(nsStringBuffer
) + aSize
<= size_t(uint32_t(-1)) &&
84 sizeof(nsStringBuffer
) + aSize
> aSize
,
85 "mStorageSize will truncate");
87 // no point in trying to save ourselves if we hit this assertion
88 NS_ASSERTION(!aHdr
->IsReadonly(), "|Realloc| attempted on readonly string");
90 // Treat this as a release and addref for refcounting purposes, since we
91 // just asserted that the refcount is 1. If we don't do that, refcount
92 // logging will claim we've leaked all sorts of stuff.
93 NS_LOG_RELEASE(aHdr
, 0, "nsStringBuffer");
95 aHdr
= (nsStringBuffer
*)realloc(aHdr
, sizeof(nsStringBuffer
) + aSize
);
97 NS_LOG_ADDREF(aHdr
, 1, "nsStringBuffer", sizeof(*aHdr
));
98 aHdr
->mStorageSize
= aSize
;
104 nsStringBuffer
* nsStringBuffer::FromString(const nsAString
& aStr
) {
105 if (!(aStr
.mDataFlags
& nsAString::DataFlags::REFCOUNTED
)) {
109 return FromData(aStr
.mData
);
112 nsStringBuffer
* nsStringBuffer::FromString(const nsACString
& aStr
) {
113 if (!(aStr
.mDataFlags
& nsACString::DataFlags::REFCOUNTED
)) {
117 return FromData(aStr
.mData
);
120 void nsStringBuffer::ToString(uint32_t aLen
, nsAString
& aStr
,
121 bool aMoveOwnership
) {
122 char16_t
* data
= static_cast<char16_t
*>(Data());
124 MOZ_DIAGNOSTIC_ASSERT(data
[aLen
] == char16_t(0),
125 "data should be null terminated");
127 nsAString::DataFlags flags
=
128 nsAString::DataFlags::REFCOUNTED
| nsAString::DataFlags::TERMINATED
;
130 if (!aMoveOwnership
) {
134 aStr
.SetData(data
, aLen
, flags
);
137 void nsStringBuffer::ToString(uint32_t aLen
, nsACString
& aStr
,
138 bool aMoveOwnership
) {
139 char* data
= static_cast<char*>(Data());
141 MOZ_DIAGNOSTIC_ASSERT(data
[aLen
] == char(0),
142 "data should be null terminated");
144 nsACString::DataFlags flags
=
145 nsACString::DataFlags::REFCOUNTED
| nsACString::DataFlags::TERMINATED
;
147 if (!aMoveOwnership
) {
151 aStr
.SetData(data
, aLen
, flags
);
154 size_t nsStringBuffer::SizeOfIncludingThisIfUnshared(
155 mozilla::MallocSizeOf aMallocSizeOf
) const {
156 return IsReadonly() ? 0 : aMallocSizeOf(this);
159 size_t nsStringBuffer::SizeOfIncludingThisEvenIfShared(
160 mozilla::MallocSizeOf aMallocSizeOf
) const {
161 return aMallocSizeOf(this);