blob: 2ad331f1a4bfebcb9935bc9edfac2ae63253333a [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)
#include "pagespeed/kernel/cache/key_value_codec.h"
#include <limits.h> // for CHAR_BIT
#include <cstddef>
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/shared_string.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
namespace {
// We can't store arbitrary keys in some caches (e.g. memcached), so
// encode the actual key in the value. Thus in the unlikely event of
// a hash collision, we can reject the mismatched full key when
// reading.
//
// We encode the length as the last two bytes. Keys of length greater than
// 65535 bytes result in an encode failure, and false returned.
const int kKeySizeOverheadBytes = 2;
const size_t kKeyMaxLength = (1 << (kKeySizeOverheadBytes * CHAR_BIT)) - 1;
} // namespace
namespace net_instaweb {
namespace key_value_codec {
// Takes a key and a value, and encodes the pair of them into key_value,
// sharing storage with value.
//
// The encoding format is [value, key, 2 bytes of key size].
bool Encode(StringPiece key, SharedString* value, SharedString* key_value) {
if (key.size() > kKeyMaxLength) {
return false;
}
uint32 key_size = key.size();
*key_value = *value;
key_value->Append(key);
uint8 ch = key_size & 0xff;
key_value->Append(reinterpret_cast<char*>(&ch), 1);
ch = (key_size >> 8) & 0xff;
key_value->Append(reinterpret_cast<char*>(&ch), 1);
return true;
}
// Takes a combined key and a value, and decodes them into key and value,
// sharing the storage with key_value.
bool Decode(SharedString* key_value, GoogleString* key, SharedString* value) {
int key_value_size = key_value->size();
if (key_value_size < kKeySizeOverheadBytes) {
return false;
}
const uint8* data = reinterpret_cast<const uint8*>(key_value->data());
int key_size = data[key_value_size - 1];
key_size <<= 8;
key_size |= data[key_value_size - 2];
key_value_size -= kKeySizeOverheadBytes; // ignore overhead now.
if (key_value_size < key_size) {
return false;
}
uint32 value_size = key_value_size - key_size;
key->assign(reinterpret_cast<const char*>(data) + value_size, key_size);
*value = *key_value; // Shares string storage, but with different prefixes.
value->RemoveSuffix(key_size + kKeySizeOverheadBytes);
return true;
}
int GetValueSizeFromKeyAndKeyValue(StringPiece key,
const SharedString& key_and_value) {
return key_and_value.size() - key.size() - kKeySizeOverheadBytes;
}
} // namespace key_value_codec
} // namespace net_instaweb