blob: 69f85db6ad79b39a2cb601941085e5e0d61a0567 [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: jmarantz@google.com (Joshua Marantz)
//
// Implements a ref-counted string class, with full sharing. This
// class does *not* implement copy-on-write semantics, however, it
// does support a unique() method for determining, prior to writing,
// whether other references exist. Thus it is feasible to implement
// copy-on-write as a layer over this class.
#include "pagespeed/kernel/base/shared_string.h"
#include <algorithm> // for std::min, std::max
#include "base/logging.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/ref_counted_ptr.h"
namespace net_instaweb {
SharedString::SharedString() : skip_(0), size_(0) {
}
SharedString::SharedString(const StringPiece& str)
: skip_(0),
size_(str.size()) {
GoogleString* storage = ref_string_.get();
str.CopyToString(storage);
}
// When constructing with a GoogleString, going through the StringPiece
// ctor above causes an extra copy compared with string implementations that
// use copy-on-write. So we make an explicit GoogleString constructor.
SharedString::SharedString(const GoogleString& str)
: ref_string_(str),
skip_(0),
size_(str.size()) {
}
// Given the two constructors above, it is ambiguous which one gets
// called when passed a string-literal, so making an explicit const char*
// constructor eliminates the ambiguity. This is likely beneficial mostly
// for tests.
SharedString::SharedString(const char* str)
: skip_(0) {
GoogleString* storage = ref_string_.get();
*storage = str;
size_ = storage->size();
}
SharedString::SharedString(const SharedString& src)
: ref_string_(src.ref_string_),
skip_(src.skip_),
size_(src.size_) {
}
SharedString& SharedString::operator=(const SharedString& src) {
if (&src != this) {
ref_string_ = src.ref_string_;
skip_ = src.skip_;
size_ = src.size_;
}
return *this;
}
StringPiece SharedString::Value() const {
const GoogleString* storage = ref_string_.get();
DCHECK_LE(size_ + skip_, static_cast<int>(storage->size()));
return StringPiece(storage->data() + skip_, size_);
}
void SharedString::Assign(const char* data, int size) {
// Note that 'str' might be a substring of the current storage, so
// avoid bugs by copying to a temp and swapping.
GoogleString temp(data, size);
ClearIfShared();
GoogleString* storage = ref_string_.get();
temp.swap(*storage);
size_ = storage->size();
}
void SharedString::UniquifyIfTruncated() {
if (size_ != (static_cast<int>(ref_string_->size()) - skip_)) {
if (unique()) {
ref_string_->resize(size_ + skip_);
} else {
*this = SharedString(Value());
DCHECK(unique());
}
}
}
void SharedString::Append(const char* new_data, size_t new_size) {
DCHECK((new_data + new_size) <= data() ||
(data() + size() < new_data))
<< "Append must be given non-overlapping strings";
UniquifyIfTruncated();
ref_string_->append(new_data, new_size);
size_ += new_size;
}
void SharedString::Extend(int new_size) {
if (size_ < new_size) {
UniquifyIfTruncated();
size_ = new_size;
ref_string_.get()->resize(size_ + skip_);
}
}
void SharedString::WriteAt(int dest_offset, const char* source, int count) {
DCHECK_LT(dest_offset, size());
DCHECK_LE(dest_offset + count, size());
if (count > size() - dest_offset) {
count = std::max(0, size() - dest_offset);
}
memcpy(mutable_data() + dest_offset, source, count);
}
void SharedString::SwapWithString(GoogleString* str) {
ClearIfShared();
GoogleString* storage = ref_string_.get();
storage->swap(*str);
skip_ = 0;
size_ = storage->size();
}
void SharedString::DetachAndClear() {
SharedString empty_string;
*this = empty_string; // Detaches other strings sharing this value.
}
void SharedString::RemovePrefix(int n) {
DCHECK_LE(n, size_);
if (n > size_) {
n = size_;
}
skip_ += n;
size_ -= n;
}
// Removes the last n characters from the string. Other linked
// SharedStrings remain linked, but are unaffected by this removal
// because each has their own skip_ and size_.
void SharedString::RemoveSuffix(int n) {
DCHECK_LE(n, size_);
size_ -= std::min(n, size_);
}
} // namespace net_instaweb