| /* |
| * 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 |