blob: 85af6872ec05bd6ebb1a005dcf85010f9dfa0802 [file] [log] [blame]
/*
* Copyright 2010 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.
#ifndef PAGESPEED_KERNEL_BASE_SHARED_STRING_H_
#define PAGESPEED_KERNEL_BASE_SHARED_STRING_H_
#include <cstddef> // for size_t
#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 {
// Reference-counted string. This class allows for shared underlying
// storage with other SharedString instances, but for trimming a
// SharedString instance's view of it via RemoveSuffix() and RemovePrefix().
class SharedString {
public:
SharedString();
explicit SharedString(const StringPiece& str);
// 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.
explicit SharedString(const GoogleString& str);
// 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.
explicit SharedString(const char* str);
// Storage-sharing occurs as a result of the copy-constructor and
// assignment operator.
SharedString(const SharedString& src);
SharedString& operator=(const SharedString& src);
// Returns the value as a StringPiece, taking into account any calls
// to RemovePrefix, RemoveSuffix, and any string-mutations due to
// Append or WriteAt on this or any other SharedStrings sharing
// storage due to the assignment operator or operator=.
StringPiece Value() const;
// Resets SharedString to be a copy of str, erasing any previous
// prefix/suffix. Calling this function detaches any connected
// SharedStrings.
//
// It is valid to assign from a value inside the SharedString. In
// other words, shared_string.Assign(shared_string.Value().substr(...))
// will work.
void Assign(StringPiece str) { Assign(str.data(), str.size()); }
void Assign(const char* data, int size);
// Appends a new string to the underlying storage. Other SharedStrings will
// not be affected by this mutation.
//
// This function tries to avoid detaching from other SharedStrings, and only
// needs to do so if this has been truncated.
//
// Unlike Assign, it is invalid to append characters managed by this
// SharedString. In other words, shared_string.Append(shared_string.Value())
// will fail.
//
// Note: Append() is not thread-safe. Concurrent accesses to any
// SharedStrings with the same storage will fail.
void Append(StringPiece str) { Append(str.data(), str.size()); }
void Append(const char* data, size_t size);
// Makes the string representation at least 'new_size' large,
// without specifying how new bytes should be filled in. Typically
// this will be followed by a call to WriteAt().
//
// This function does *not* detach other SharedStrings -- the
// underlying storage will still be shared. Consequently this
// function does not shrink strings, as that could invalidate
// trimmed SharedStrings sharing the storage.
//
// If this method is called on a truncated SharedString, then it will be
// detached prior to extending it.
void Extend(int new_size);
// Swaps storage with the the passed-in string, detaching from any other
// previously-linked SharedStrings.
void SwapWithString(GoogleString* str);
// Clears the contents of the string, and erases any removed prefix
// or suffix, detaching from any other previously-linked SharedStrings.
void DetachAndClear();
// Removes the first n characters from the string. Other linked
// SharedStrings remain linked, but are unaffected by this removal
// because each has its own skip_ and size_.
void RemovePrefix(int n);
// Removes the last n characters from the string. Other linked
// SharedStrings remain linked, but are unaffected by this removal
// because each has its own skip_ and size_.
void RemoveSuffix(int n);
// Computes the size, taking into account any removed prefix or suffix.
int size() const { return size_; }
bool empty() const { return size_ == 0; }
const char* data() const { return ref_string_->data() + skip_; }
// WriteAt allows mutation of the underlying string data. The
// string must already be sized as needed via previous Append() or
// Extend() calls. Mutations done via this method will affect all
// references to the underlying storage.
void WriteAt(int dest_offset, const char* source, int count);
// Disassociates this SharedString with any others that have linked
// the same storage. Retains the same string value.
void DetachRetainingContent() {
if (!unique()) {
*this = SharedString(Value());
DCHECK(unique());
}
}
// Determines whether this SharedString shares storage from other
// SharedStrings.
bool unique() const { return ref_string_.unique(); }
// Determines whether RemovePrefix or RemoveSuffix has every been called
// on this SharedString. Note that other SharedStrings sharing the
// same storage as this may be trimmed differently.
bool trimmed() const {
return size_ != static_cast<int>(ref_string_->size());
}
// Returns back a GoogleString* representation for the contained value.
//
// This is makes sense to call only if the string is not trimmed. If
// RemovePrefix or RemoveSuffix has been called on this SharedString, the
// returned string may have extra characters in it.
//
// Note: we suggest against using this routine. It is better to consume
// the data via the StringPiece returned from Value().
//
// This routine is, however, useful to call from tests to determine
// storage uniqueness.
const GoogleString* StringValue() const {
return ref_string_.get();
}
// Determines whether this and that share the same storage.
bool SharesStorage(const SharedString& that) const {
return ref_string_.get() == that.ref_string_.get();
}
private:
void UniquifyIfTruncated();
char* mutable_data() { return &(*ref_string_.get())[0] + skip_; }
void ClearIfShared() {
if (!unique()) {
DetachAndClear();
}
}
RefCountedObj<GoogleString> ref_string_;
int skip_; // Number of bytes to skip at the beginning of the string.
int size_; // Number of bytes visible in the current view.
};
} // namespace net_instaweb
#endif // PAGESPEED_KERNEL_BASE_SHARED_STRING_H_