Save previous version

This commit is contained in:
Victor Shcherb 2011-10-28 16:01:00 +02:00
parent 1f5d66ed32
commit f1a85e499b
111 changed files with 83204 additions and 304 deletions

View file

@ -10,8 +10,14 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
</natures>
</projectDescription>

View file

@ -1,6 +1,9 @@
option java_package = "net.osmand.binary";
//protoc --java_out=../.. osmand_odb.proto
// C++ # cd OsmAnd
// # protoc --proto_path=../DataExtractionOSM/src --cpp_out=jni/osmand/proto ../DataExtractionOSM/src/osmand_odb.proto
//
// STORAGE LAYER: Storing primitives.
//

View file

@ -28,13 +28,15 @@
<tool id="cdt.managedbuild.tool.gnu.archiver.base.2094589046" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
<tool id="com.android.tool.compiler.g++.1543637239" name="Android G++" superClass="com.android.tool.compiler.g++">
<option id="gnu.cpp.compiler.option.include.paths.757537552" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
<listOptionValue builtIn="false" value="/home/victor/projects/android-ndk-r6b/sources/cxx-stl/stlport/stlport"/>
<listOptionValue builtIn="false" value="/home/victor/projects/android-ndk-r6b/sources/cxx-stl/gnu-libstdc++/include"/>
<listOptionValue builtIn="false" value="/home/victor/projects/android/external/skia/include/core"/>
<listOptionValue builtIn="false" value="/home/victor/projects/android/frameworks/base/include"/>
<listOptionValue builtIn="false" value="/home/victor/projects/android/external/skia/include/effects"/>
<listOptionValue builtIn="false" value="/home/victor/projects/android-ndk-r6b/sources/cxx-stl/gnu-libstdc++/libs/armeabi/include"/>
<listOptionValue builtIn="false" value="/home/victor/projects/android-ndk-r6b/platforms/android-8/arch-arm/usr/include/linux/"/>
<listOptionValue builtIn="false" value="/home/victor/projects/android-ndk-r6b/sources/cxx-stl/system/include"/>
<listOptionValue builtIn="false" value="/home/victor/projects/android-ndk-r6b/sources/cxx-stl/stlport/stlport"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/OsmAnd/jni/protobuf}&quot;"/>
</option>
<inputType id="com.android.tool.compiler.g++.input.478508689" superClass="com.android.tool.compiler.g++.input"/>
</tool>

View file

@ -1,65 +1 @@
LOCAL_PATH := $(call my-dir)
#include $(CLEAR_VARS)
#LOCAL_MODULE := skia2.2
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := libskia2.2.so
#LOCAL_PRELINK_MODULE := false
#
#include $(PREBUILT_SHARED_LIBRARY)
#include $(CLEAR_VARS)
#LOCAL_MODULE := skia_built
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := skia_built.a
#include $(PREBUILT_STATIC_LIBRARY)
#
#include $(CLEAR_VARS)
#LOCAL_MODULE := png
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := libpng.a
#include $(PREBUILT_STATIC_LIBRARY)
#
#include $(CLEAR_VARS)
#LOCAL_MODULE := gif
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := libgif.a
#include $(PREBUILT_STATIC_LIBRARY)
#
#include $(CLEAR_VARS)
#LOCAL_MODULE := ft2
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := libft2.a
#include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
ANDROID_FOLDER := /home/victor/projects/android/
SKIA_FOLDER := $(ANDROID_FOLDER)/external/skia
SKIA_SRC := skia
LOCAL_MODULE := osmand
LOCAL_C_INCLUDES := $(LOCAL_PATH)/jni \
$(SKIA_FOLDER)/include/core \
$(SKIA_FOLDER)/include/utils \
$(SKIA_FOLDER)/include/config \
$(SKIA_FOLDER)/include/effects \
$(SKIA_FOLDER)/include/utils/android \
$(SKIA_FOLDER)/src/core \
$(ANDROID_FOLDER)/system/core/include \
$(ANDROID_FOLDER)/frameworks/base/include
LOCAL_SRC_FILES := osmand/rendering.cpp
LOCAL_CFLAGS := -Wall -g
# in that case libskia_2.2.so should be in NDK folder to be properly built
LOCAL_LDLIBS := -llog -lcutils -lskia_2.2
##LOCAL_LDLIBS := -ldl -llog -lcutils
#LOCAL_STATIC_LIBRARIES := skia_built gif png ft2
#LOCAL_SHARED_LIBRARIES := skia2.2
#LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
include $(all-subdir-makefiles)

View file

@ -0,0 +1,74 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#include $(CLEAR_VARS)
#LOCAL_MODULE := skia2.2
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := ../libskia2.2.so
#LOCAL_PRELINK_MODULE := false
#
#include $(PREBUILT_SHARED_LIBRARY)
#include $(CLEAR_VARS)
#LOCAL_MODULE := skia_built
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := ../skia_built.a
#include $(PREBUILT_STATIC_LIBRARY)
#
#include $(CLEAR_VARS)
#LOCAL_MODULE := png
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := ../libpng.a
#include $(PREBUILT_STATIC_LIBRARY)
#
#include $(CLEAR_VARS)
#LOCAL_MODULE := gif
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := ../libgif.a
#include $(PREBUILT_STATIC_LIBRARY)
#
#include $(CLEAR_VARS)
#LOCAL_MODULE := ft2
#NDK_MODULE_PATH := $(LOCAL_PATH)
#LOCAL_SRC_FILES := ../libft2.a
#include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
ANDROID_FOLDER := /home/victor/projects/android/
#PROTOBUF_FOLDER := /home/victor/projects/OsmAnd/libs/protobuf-2.3.0/src
PROTOBUF_FOLDER := $(LOCAL_PATH)/../protobuf
SKIA_FOLDER := $(ANDROID_FOLDER)/external/skia
SKIA_SRC := skia
LOCAL_MODULE := osmand
LOCAL_C_INCLUDES := $(LOCAL_PATH)/jni \
$(PROTOBUF_FOLDER) \
$(SKIA_FOLDER)/include/core \
$(SKIA_FOLDER)/include/utils \
$(SKIA_FOLDER)/include/config \
$(SKIA_FOLDER)/include/effects \
$(SKIA_FOLDER)/include/utils/android \
$(SKIA_FOLDER)/src/core \
$(ANDROID_FOLDER)/system/core/include \
$(ANDROID_FOLDER)/frameworks/base/include
LOCAL_CPP_EXTENSION := .cpp
LOCAL_SRC_FILES := common.cpp \
rendering.cpp \
proto/osmand_odb.pb.cpp binaryRead.cpp
LOCAL_CFLAGS := -Wall -g -DGOOGLE_PROTOBUF_NO_RTTI
# in that case libskia_2.2.so should be in NDK folder to be properly built
LOCAL_LDLIBS := -llog -lcutils -lskia_2.2
##LOCAL_LDLIBS := -ldl -llog -lcutils
LOCAL_STATIC_LIBRARIES := proto
#LOCAL_STATIC_LIBRARIES := skia_built gif png ft2
#LOCAL_SHARED_LIBRARIES := skia2.2
#LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)

View file

@ -0,0 +1,132 @@
#include <jni.h>
#include <math.h>
#include <android/log.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <fstream>
#include <map>
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/wire_format_lite.h"
#include "google/protobuf/wire_format_lite.cc"
#include "google/protobuf/wire_format.h"
#include "common.h"
#include "proto/osmand_odb.pb.h"
char errorMsg[1024];
using namespace google::protobuf;
using namespace google::protobuf::internal;
struct BinaryMapFile;
std::map<std::string, BinaryMapFile*> openFiles;
inline bool readInt(io::CodedInputStream* input, uint32* sz) {
uint8 buf[4];
if(!input->ReadRaw(buf, 4)){
return false;
}
*sz = ((buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + (buf[3] << 0));
return true;
}
inline bool skipFixed32(io::CodedInputStream* input){
uint32 sz;
if(!readInt(input, &sz)) {
return false;
}
return input->Skip(sz);
}
#define SKIPFIELDS if (WireFormatLite::GetTagWireType(tag) ==WireFormatLite::WIRETYPE_END_GROUP) { return true; } \
if (WireFormatLite::GetTagWireType(tag) ==WireFormatLite::WIRETYPE_FIXED32_LENGTH_DELIMITED) { if(!skipFixed32(input)) return false; } \
if(!WireFormat::SkipField(input, tag, NULL)) {return false; }
struct BinaryMapFile {
io::FileInputStream* input;
std::string inputName;
~BinaryMapFile() {
input->Close();
delete input;
}
};
bool initMapStructure(io::CodedInputStream* input, BinaryMapFile* file) {
#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
uint32 tag;
uint32 version = -1;
uint32 versionConfirm = -2;
while ((tag = input->ReadTag()) != 0) {
switch (WireFormatLite::GetTagFieldNumber(tag)) {
// required uint32 version = 1;
case OsmAndStructure::kVersionFieldNumber : {
DO_((WireFormatLite::ReadPrimitive<uint32, WireFormatLite::TYPE_UINT32>(input, &version)));
break;
}
// case OsmAndStructure::kMapIndexFieldNumber : {
// // TODO
// break;
// }
case OsmAndStructure::kVersionConfirmFieldNumber : {
DO_((WireFormatLite::ReadPrimitive<uint32, WireFormatLite::TYPE_UINT32>(input, &versionConfirm)));
break;
}
default: {
SKIPFIELDS;
break;
}
}
}
if (version != versionConfirm) {
__android_log_print(ANDROID_LOG_WARN, "net.osmand",
"Corrupted file. It should be ended as it starts with version");
return false;
}
#undef DO_
return true;
}
void loadJniBinaryRead() {
jstring js = env->NewStringUTF("Privet");
__android_log_print(ANDROID_LOG_ERROR, "net.osmand", getString(js).c_str());
}
extern "C"
JNIEXPORT jboolean JNICALL Java_net_osmand_plus_render_NativeOsmandLibrary_initBinaryMapFile(JNIEnv* ienv,
jstring path) {
std::string inputName = getString(path);
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
std::map<std::string, BinaryMapFile*>::iterator iterator;
if ((iterator = openFiles.find(inputName)) != openFiles.end()) {
delete iterator->second;
openFiles.erase(iterator);
}
FILE* file = fopen(inputName.c_str(), "r");
if (file == NULL) {
sprintf(errorMsg, "File not initialised : %s", inputName.c_str());
__android_log_print(ANDROID_LOG_WARN, "net.osmand", errorMsg);
return false;
}
BinaryMapFile* mapFile = new BinaryMapFile();
mapFile->input = new io::FileInputStream(fileno(file));
io::CodedInputStream cis (mapFile->input);
if (!initMapStructure(&cis, mapFile)) {
sprintf(errorMsg, "File not initialised : %s", inputName.c_str());
__android_log_print(ANDROID_LOG_WARN, "net.osmand", errorMsg);
delete mapFile;
return false;
}
mapFile->inputName = inputName;
openFiles.insert(std::pair<std::string, BinaryMapFile*>(inputName, mapFile));
return true;
}

View file

@ -1,26 +1,32 @@
#ifndef _OSMAND_COMMON
#define _OSMAND_COMMON
#include <jni.h>
#include <common.h>
#include <string>
#include <vector>
#include <hash_map>
#include <SkPath.h>
#include <SkBitmap.h>
#define DEBUG_NAT_OPERATIONS
#ifdef DEBUG_NAT_OPERATIONS
#define NAT_COUNT(rc, op) rc->nativeOperations.pause(); op; rc->nativeOperations.start()
#else
#define NAT_COUNT(rc, op) op;
#endif
JNIEnv* env;
const std::string EMPTY_STRING;
const int WHITE_COLOR = -1;
const int BLACK_COLOR = 0xff000000;
extern void loadJniCommon();
extern void loadJniBinaryRead();
extern void loadJNIRenderingRules();
extern void loadJniMapObjects();
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
if(vm->GetEnv((void **)&env, JNI_VERSION_1_4)){
return JNI_ERR; /* JNI version not supported */
}
loadJniCommon();
loadJNIRenderingRules();
loadJniMapObjects();
loadJniBinaryRead();
return JNI_VERSION_1_4;
}
jclass RenderingContextClass;
jfieldID RenderingContext_interrupted;
@ -33,158 +39,53 @@ jclass globalRef(jobject o)
return (jclass) env->NewGlobalRef( o);
}
class TextDrawInfo {
public :
std::string text;
TextDrawInfo(std::string itext) {
text = itext;
drawOnPath = false;
path = NULL;
pathRotate = 0;
}
SkRect bounds;
float centerX;
float centerY;
float textSize ;
float minDistance ;
int textColor;
int textShadow ;
uint textWrap ;
bool bold ;
std::string shieldRes;
int textOrder;
bool drawOnPath;
SkPath* path;
float pathRotate;
float vOffset ;
float hOffset ;
~TextDrawInfo() {
if (path != NULL) {
delete path;
}
}
};
struct IconDrawInfo {
SkBitmap* bmp;
float x;
float y;
};
jfieldID getFid(jclass cls,const char* fieldName, const char* sig )
{
return env->GetFieldID( cls, fieldName, sig);
}
class watcher {
long elapsedTime;
bool enableFlag;
// timeval startInit;
// timeval endInit;
timespec startInit;
timespec endInit;
bool run;
public:
watcher() {
elapsedTime = 0;
enableFlag = true;
run = false;
watcher::watcher() {
elapsedTime = 0;
enableFlag = true;
run = false;
}
void watcher::enable() {
enableFlag = true;
}
void watcher::disable() {
pause();
enableFlag = false;
}
void watcher::start() {
if (!enableFlag) {
return;
}
void enable(){
enableFlag = true;
}
void disable(){
pause();
enableFlag = false;
}
void start() {
if(!enableFlag){
return;
}
if (!run) {
clock_gettime(CLOCK_MONOTONIC, &startInit);
if (!run) {
clock_gettime(CLOCK_MONOTONIC, &startInit);
// gettimeofday(&startInit, NULL);
}
run = true;
}
void pause() {
if (!run) {
return;
}
clock_gettime(CLOCK_MONOTONIC, &endInit );
// gettimeofday(&endInit, NULL);
int sec = endInit.tv_sec - startInit.tv_sec;
if(sec > 0){
elapsedTime += 1e9 * sec;
}
elapsedTime += endInit.tv_nsec - startInit.tv_nsec ;
run = true;
}
void watcher::pause() {
if (!run) {
return;
}
clock_gettime(CLOCK_MONOTONIC, &endInit);
// gettimeofday(&endInit, NULL);
int sec = endInit.tv_sec - startInit.tv_sec;
if (sec > 0) {
elapsedTime += 1e9 * sec;
}
elapsedTime += endInit.tv_nsec - startInit.tv_nsec;
// elapsedTime += (endInit.tv_sec * 1000 + endInit.tv_usec / 1000)
// - (startInit.tv_sec * 1000 + startInit.tv_usec / 1000);
run = false;
}
int getElapsedTime() {
pause();
return elapsedTime / 1e6;
}
};
struct RenderingContext {
jobject originalRC;
jobject androidContext;
std::vector<TextDrawInfo*> textToDraw;
std::vector<IconDrawInfo> iconsToDraw;
bool highResMode;
float mapTextSize;
float density;
float leftX;
float topY;
int width;
int height;
int zoom;
float rotate;
float tileDivisor;
// debug purpose
int pointCount;
int pointInsideCount;
int visible;
int allObjects;
watcher textRendering;
watcher nativeOperations;
// use to calculate points
float calcX;
float calcY;
float cosRotateTileSize;
float sinRotateTileSize;
int shadowRenderingMode;
// not expect any shadow
int shadowLevelMin;
int shadowLevelMax;
bool interrupted() {
return env->GetBooleanField(originalRC, RenderingContext_interrupted);
}
~RenderingContext() {
for (uint i = 0; i < textToDraw.size(); i++) {
delete textToDraw.at(i);
}
}
};
run = false;
}
int watcher::getElapsedTime() {
pause();
return elapsedTime / 1e6;
}
@ -202,9 +103,7 @@ std::string getStringField(jobject o, jfieldID fid)
return res;
}
std::string getStringMethod(jobject o, jmethodID fid)
{
jstring st = (jstring) env->CallObjectMethod(o, fid);
std::string getString(jstring st) {
if (st == NULL) {
return EMPTY_STRING;
}
@ -215,17 +114,14 @@ std::string getStringMethod(jobject o, jmethodID fid)
return res;
}
std::string getStringMethod(jobject o, jmethodID fid)
{
return getString((jstring) env->CallObjectMethod(o, fid));
}
std::string getStringMethod(jobject o, jmethodID fid, int i)
{
jstring st = (jstring) env->CallObjectMethod(o, fid, i);
if (st == NULL) {
return EMPTY_STRING;
}
const char* utf = env->GetStringUTFChars(st, NULL);
std::string res(utf);
env->ReleaseStringUTFChars(st, utf);
env->DeleteLocalRef(st);
return res;
return getString((jstring) env->CallObjectMethod(o, fid, i));
}
float getDensityValue(RenderingContext* rc, float val) {
@ -268,11 +164,12 @@ SkBitmap* getCachedBitmap(RenderingContext* rc, std::string js)
return res;
}
void loadJniCommon(jobject rc) {
RenderingContextClass = globalRef(env->GetObjectClass(rc));
void loadJniCommon() {
RenderingContextClass = globalRef(env->FindClass("net/osmand/plus/render/OsmandRenderer$RenderingContext"));
RenderingContext_interrupted = getFid(RenderingContextClass, "interrupted", "Z");
RenderingIconsClass = globalRef(env->FindClass("net/osmand/render/RenderingRule"));
RenderingIconsClass = globalRef(env->FindClass("net/osmand/plus/render/RenderingIcons"));
RenderingIcons_getIcon = env->GetStaticMethodID(RenderingIconsClass, "getIcon",
"(Landroid/content/Context;Ljava/lang/String;)Landroid/graphics/Bitmap;");
@ -286,4 +183,52 @@ void unloadJniCommon() {
void copyRenderingContext(jobject orc, RenderingContext* rc)
{
rc->leftX = env->GetFloatField( orc, getFid( RenderingContextClass, "leftX", "F" ) );
rc->topY = env->GetFloatField( orc, getFid( RenderingContextClass, "topY", "F" ) );
rc->width = env->GetIntField( orc, getFid( RenderingContextClass, "width", "I" ) );
rc->height = env->GetIntField( orc, getFid( RenderingContextClass, "height", "I" ) );
rc->zoom = env->GetIntField( orc, getFid( RenderingContextClass, "zoom", "I" ) );
rc->rotate = env->GetFloatField( orc, getFid( RenderingContextClass, "rotate", "F" ) );
rc->tileDivisor = env->GetFloatField( orc, getFid( RenderingContextClass, "tileDivisor", "F" ) );
rc->pointCount = env->GetIntField( orc, getFid( RenderingContextClass, "pointCount", "I" ) );
rc->pointInsideCount = env->GetIntField( orc, getFid( RenderingContextClass, "pointInsideCount", "I" ) );
rc->visible = env->GetIntField( orc, getFid( RenderingContextClass, "visible", "I" ) );
rc->allObjects = env->GetIntField( orc, getFid( RenderingContextClass, "allObjects", "I" ) );
rc->cosRotateTileSize = env->GetFloatField( orc, getFid( RenderingContextClass, "cosRotateTileSize", "F" ) );
rc->sinRotateTileSize = env->GetFloatField( orc, getFid( RenderingContextClass, "sinRotateTileSize", "F" ) );
rc->density = env->GetFloatField( orc, getFid( RenderingContextClass, "density", "F" ) );
rc->highResMode = env->GetBooleanField( orc, getFid( RenderingContextClass, "highResMode", "Z" ) );
rc->mapTextSize = env->GetFloatField( orc, getFid( RenderingContextClass, "mapTextSize", "F" ) );
rc->shadowRenderingMode = env->GetIntField( orc, getFid( RenderingContextClass, "shadowRenderingMode", "I" ) );
rc->shadowLevelMin = env->GetIntField( orc, getFid( RenderingContextClass, "shadowLevelMin", "I" ) );
rc->shadowLevelMax = env->GetIntField( orc, getFid( RenderingContextClass, "shadowLevelMax", "I" ) );
rc->androidContext = env->GetObjectField(orc, getFid( RenderingContextClass, "ctx", "Landroid/content/Context;"));
rc->originalRC = orc;
}
void mergeRenderingContext(jobject orc, RenderingContext* rc)
{
env->SetIntField( orc, getFid(RenderingContextClass, "pointCount", "I" ) , rc->pointCount);
env->SetIntField( orc, getFid(RenderingContextClass, "pointInsideCount", "I" ) , rc->pointInsideCount);
env->SetIntField( orc, getFid(RenderingContextClass, "visible", "I" ) , rc->visible);
env->SetIntField( orc, getFid(RenderingContextClass, "allObjects", "I" ) , rc->allObjects);
env->SetIntField( orc, getFid(RenderingContextClass, "textRenderingTime", "I" ) , rc->textRendering.getElapsedTime());
env->DeleteLocalRef(rc->androidContext);
}
#endif

162
OsmAnd/jni/osmand/common.h Normal file
View file

@ -0,0 +1,162 @@
#ifndef _OSMAND_COMMON_H
#define _OSMAND_COMMON_H
#include <jni.h>
#include <string>
#include <vector>
#include <hash_map>
#include <SkPath.h>
#include <SkBitmap.h>
// Constants
#define DEBUG_NAT_OPERATIONS
#ifdef DEBUG_NAT_OPERATIONS
#define NAT_COUNT(rc, op) rc->nativeOperations.pause(); op; rc->nativeOperations.start()
#else
#define NAT_COUNT(rc, op) op;
#endif
const std::string EMPTY_STRING;
const int WHITE_COLOR = -1;
const int BLACK_COLOR = 0xff000000;
extern JNIEnv* env;
// JNI Helpers
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);
std::string getString(jstring st);
std::string getStringMethod(jobject o, jmethodID fid, int i);
std::string getStringMethod(jobject o, jmethodID fid);
std::string getStringField(jobject o, jfieldID fid);
jclass globalRef(jobject o);
jfieldID getFid(jclass cls,const char* fieldName, const char* sig );
class watcher {
private:
long elapsedTime;
bool enableFlag;
// timeval startInit;
// timeval endInit;
timespec startInit;
timespec endInit;
bool run;
public:
watcher();
void enable();
void disable();
void start();
void pause();
int getElapsedTime();
};
// Rendering context methods
class TextDrawInfo {
public :
std::string text;
TextDrawInfo(std::string itext) {
text = itext;
drawOnPath = false;
path = NULL;
pathRotate = 0;
}
SkRect bounds;
float centerX;
float centerY;
float textSize ;
float minDistance ;
int textColor;
int textShadow ;
uint textWrap ;
bool bold ;
std::string shieldRes;
int textOrder;
bool drawOnPath;
SkPath* path;
float pathRotate;
float vOffset ;
float hOffset ;
~TextDrawInfo() {
if (path != NULL) {
delete path;
}
}
};
struct IconDrawInfo {
SkBitmap* bmp;
float x;
float y;
};
extern jfieldID RenderingContext_interrupted;
struct RenderingContext {
jobject originalRC;
jobject androidContext;
std::vector<TextDrawInfo*> textToDraw;
std::vector<IconDrawInfo> iconsToDraw;
bool highResMode;
float mapTextSize;
float density;
float leftX;
float topY;
int width;
int height;
int zoom;
float rotate;
float tileDivisor;
// debug purpose
int pointCount;
int pointInsideCount;
int visible;
int allObjects;
watcher textRendering;
watcher nativeOperations;
// use to calculate points
float calcX;
float calcY;
float cosRotateTileSize;
float sinRotateTileSize;
int shadowRenderingMode;
// not expect any shadow
int shadowLevelMin;
int shadowLevelMax;
bool interrupted() {
return env->GetBooleanField(originalRC, RenderingContext_interrupted);
}
~RenderingContext() {
for (uint i = 0; i < textToDraw.size(); i++) {
delete textToDraw.at(i);
}
}
};
void copyRenderingContext(jobject orc, RenderingContext* rc);
void mergeRenderingContext(jobject orc, RenderingContext* rc);
float getDensityValue(RenderingContext* rc, float val);
SkBitmap* getCachedBitmap(RenderingContext* rc, std::string js);
SkBitmap* getNativeBitmap(jobject bmpObj);
#endif /*_OSMAND_COMMON_H*/

View file

@ -1,7 +1,7 @@
#include <jni.h>
#include <vector>
#include "common.cpp"
#include "common.h"
jclass MultiPolygonClass;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@
#include <vector>
#include <hash_map>
#include "common.cpp"
#include "common.h"
jclass ListClass;
jmethodID List_size;

View file

@ -17,15 +17,13 @@
#include "SkPaint.h"
#include "SkPath.h"
#include "common.cpp"
#include "common.h"
#include "renderRules.cpp"
#include "textdraw.cpp"
#include "mapObjects.cpp"
char debugMessage[1024];
void calcPoint(MapDataObject* mObj, jint ind, RenderingContext* rc) {
rc->pointCount++;
@ -543,66 +541,6 @@ void drawObject(RenderingContext* rc, BaseMapDataObject* mapObject, SkCanvas* cv
}
void copyRenderingContext(jobject orc, RenderingContext* rc)
{
rc->leftX = env->GetFloatField( orc, getFid( RenderingContextClass, "leftX", "F" ) );
rc->topY = env->GetFloatField( orc, getFid( RenderingContextClass, "topY", "F" ) );
rc->width = env->GetIntField( orc, getFid( RenderingContextClass, "width", "I" ) );
rc->height = env->GetIntField( orc, getFid( RenderingContextClass, "height", "I" ) );
rc->zoom = env->GetIntField( orc, getFid( RenderingContextClass, "zoom", "I" ) );
rc->rotate = env->GetFloatField( orc, getFid( RenderingContextClass, "rotate", "F" ) );
rc->tileDivisor = env->GetFloatField( orc, getFid( RenderingContextClass, "tileDivisor", "F" ) );
rc->pointCount = env->GetIntField( orc, getFid( RenderingContextClass, "pointCount", "I" ) );
rc->pointInsideCount = env->GetIntField( orc, getFid( RenderingContextClass, "pointInsideCount", "I" ) );
rc->visible = env->GetIntField( orc, getFid( RenderingContextClass, "visible", "I" ) );
rc->allObjects = env->GetIntField( orc, getFid( RenderingContextClass, "allObjects", "I" ) );
rc->cosRotateTileSize = env->GetFloatField( orc, getFid( RenderingContextClass, "cosRotateTileSize", "F" ) );
rc->sinRotateTileSize = env->GetFloatField( orc, getFid( RenderingContextClass, "sinRotateTileSize", "F" ) );
rc->density = env->GetFloatField( orc, getFid( RenderingContextClass, "density", "F" ) );
rc->highResMode = env->GetBooleanField( orc, getFid( RenderingContextClass, "highResMode", "Z" ) );
rc->mapTextSize = env->GetFloatField( orc, getFid( RenderingContextClass, "mapTextSize", "F" ) );
rc->shadowRenderingMode = env->GetIntField( orc, getFid( RenderingContextClass, "shadowRenderingMode", "I" ) );
rc->shadowLevelMin = env->GetIntField( orc, getFid( RenderingContextClass, "shadowLevelMin", "I" ) );
rc->shadowLevelMax = env->GetIntField( orc, getFid( RenderingContextClass, "shadowLevelMax", "I" ) );
rc->androidContext = env->GetObjectField(orc, getFid( RenderingContextClass, "ctx", "Landroid/content/Context;"));
rc->originalRC = orc;
}
void mergeRenderingContext(jobject orc, RenderingContext* rc)
{
env->SetIntField( orc, getFid(RenderingContextClass, "pointCount", "I" ) , rc->pointCount);
env->SetIntField( orc, getFid(RenderingContextClass, "pointInsideCount", "I" ) , rc->pointInsideCount);
env->SetIntField( orc, getFid(RenderingContextClass, "visible", "I" ) , rc->visible);
env->SetIntField( orc, getFid(RenderingContextClass, "allObjects", "I" ) , rc->allObjects);
env->SetIntField( orc, getFid(RenderingContextClass, "textRenderingTime", "I" ) , rc->textRendering.getElapsedTime());
env->DeleteLocalRef(rc->androidContext);
}
void loadLibrary(jobject rc) {
loadJniCommon(rc);
loadJNIRenderingRules();
loadJniMapObjects();
}
void unloadLibrary() {
unloadJniMapObjects();
unloadJniRenderRules();
unloadJniCommon();
}
void drawIconsOverCanvas(RenderingContext* rc, SkCanvas* canvas)
{
int skewConstant = (int) getDensityValue(rc, 16);
@ -759,10 +697,6 @@ extern "C" {
JNIEXPORT jstring JNICALL Java_net_osmand_plus_render_NativeOsmandLibrary_generateRendering( JNIEnv* ienv,
jobject obj, jobject renderingContext, jobjectArray binaryMapDataObjects, jobject bmpObj,
jboolean useEnglishNames, jobject renderingRuleSearchRequest, jint defaultColor) {
if(!env) {
env = ienv;
loadLibrary(renderingContext);
}
SkBitmap* bmp = getNativeBitmap(bmpObj);
sprintf(debugMessage, "Image w:%d h:%d !", bmp->width(), bmp->height());
__android_log_print(ANDROID_LOG_WARN, "net.osmand", debugMessage);

View file

@ -10,7 +10,7 @@
#include "SkPaint.h"
#include "SkPath.h"
#include "common.cpp"
#include "common.h"
#include "renderRules.cpp"
const char REF_CHAR = ((char)0x0019);

View file

@ -0,0 +1,56 @@
LOCAL_PATH := $(call my-dir)
CC_LITE_SRC_FILES := \
google/protobuf/stubs/common.cc \
google/protobuf/stubs/once.cc \
google/protobuf/stubs/hash.cc \
google/protobuf/stubs/hash.h \
google/protobuf/stubs/map-util.h \
google/protobuf/stubs/stl_util-inl.h \
google/protobuf/extension_set.cc \
google/protobuf/generated_message_util.cc \
google/protobuf/message_lite.cc \
google/protobuf/repeated_field.cc \
google/protobuf/wire_format_lite.cc \
google/protobuf/io/coded_stream.cc \
google/protobuf/io/coded_stream_inl.h \
google/protobuf/io/zero_copy_stream.cc \
google/protobuf/io/zero_copy_stream_impl_lite.cc
include $(CLEAR_VARS)
LOCAL_MODULE := proto
LOCAL_MODULE_TAGS := optional
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := $(CC_LITE_SRC_FILES) \
google/protobuf/stubs/strutil.cc \
google/protobuf/stubs/strutil.h \
google/protobuf/stubs/substitute.cc \
google/protobuf/stubs/substitute.h \
google/protobuf/stubs/structurally_valid.cc \
google/protobuf/descriptor.cc \
google/protobuf/descriptor.pb.cc \
google/protobuf/descriptor_database.cc \
google/protobuf/dynamic_message.cc \
google/protobuf/extension_set_heavy.cc \
google/protobuf/generated_message_reflection.cc \
google/protobuf/message.cc \
google/protobuf/reflection_ops.cc \
google/protobuf/service.cc \
google/protobuf/text_format.cc \
google/protobuf/unknown_field_set.cc \
google/protobuf/wire_format.cc \
google/protobuf/io/gzip_stream.cc \
google/protobuf/io/printer.cc \
google/protobuf/io/tokenizer.cc \
google/protobuf/io/zero_copy_stream_impl.cc \
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI
LOCAL_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)

View file

@ -0,0 +1,163 @@
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* the name of <hash_set> */
#define HASH_MAP_CLASS unordered_map
/* the location of <hash_map> */
#define HASH_MAP_H <tr1/unordered_map>
/* the namespace of hash_map/hash_set */
#define HASH_NAMESPACE std::tr1
/* the name of <hash_set> */
#define HASH_SET_CLASS unordered_set
/* the location of <hash_set> */
#define HASH_SET_H <tr1/unordered_set>
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define to 1 if you have the `ftruncate' function. */
#define HAVE_FTRUNCATE 1
#if defined(ANDROID)
/*
* TODO: Figure out how to use stlport unordered_map and set.
* For some reason they don't work when I try to point the
* HASH_MAP_H and HASH_SET_H to the stlport files, I get
* compile timer errors.
*/
/* define if the compiler has hash_map */
#undef HAVE_HASH_MAP
/* define if the compiler has hash_set */
#undef HAVE_HASH_SET
#else
/* define if the compiler has hash_map */
#define HAVE_HASH_MAP 1
/* define if the compiler has hash_set */
#define HAVE_HASH_SET 1
#endif
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the `memset' function. */
#define HAVE_MEMSET 1
/* Define to 1 if you have the `mkdir' function. */
#define HAVE_MKDIR 1
/* Define if you have POSIX threads libraries and header files. */
#define HAVE_PTHREAD 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the `strchr' function. */
#define HAVE_STRCHR 1
/* Define to 1 if you have the `strerror' function. */
#define HAVE_STRERROR 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the `strtol' function. */
#define HAVE_STRTOL 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Enable classes using zlib compression. */
#define HAVE_ZLIB 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Name of package */
#define PACKAGE "protobuf"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "protobuf@googlegroups.com"
/* Define to the full name of this package. */
#define PACKAGE_NAME "Protocol Buffers"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "Protocol Buffers 2.3.0"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "protobuf"
/* Define to the version of this package. */
#define PACKAGE_VERSION "2.3.0"
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
/* #undef PTHREAD_CREATE_JOINABLE */
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "2.3.0"
/* Define to 1 if on AIX 3.
System headers sometimes define this.
We just want to avoid a redefinition error message. */
#ifndef _ALL_SOURCE
/* # undef _ALL_SOURCE */
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/* Define to 1 if on MINIX. */
/* #undef _MINIX */
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
/* #undef _POSIX_1_SOURCE */
/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */
/* Enable extensions on Solaris. */
#ifndef __EXTENSIONS__
# define __EXTENSIONS__ 1
#endif
#ifndef _POSIX_PTHREAD_SEMANTICS
# define _POSIX_PTHREAD_SEMANTICS 1
#endif
#ifndef _TANDEM_SOURCE
# define _TANDEM_SOURCE 1
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,433 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// The messages in this file describe the definitions found in .proto files.
// A valid .proto file can be translated directly to a FileDescriptorProto
// without any other information (e.g. without reading its imports).
package google.protobuf;
option java_package = "com.google.protobuf";
option java_outer_classname = "DescriptorProtos";
// descriptor.proto must be optimized for speed because reflection-based
// algorithms don't work during bootstrapping.
option optimize_for = SPEED;
// The protocol compiler can output a FileDescriptorSet containing the .proto
// files it parses.
message FileDescriptorSet {
repeated FileDescriptorProto file = 1;
}
// Describes a complete .proto file.
message FileDescriptorProto {
optional string name = 1; // file name, relative to root of source tree
optional string package = 2; // e.g. "foo", "foo.bar", etc.
// Names of files imported by this file.
repeated string dependency = 3;
// All top-level definitions in this file.
repeated DescriptorProto message_type = 4;
repeated EnumDescriptorProto enum_type = 5;
repeated ServiceDescriptorProto service = 6;
repeated FieldDescriptorProto extension = 7;
optional FileOptions options = 8;
}
// Describes a message type.
message DescriptorProto {
optional string name = 1;
repeated FieldDescriptorProto field = 2;
repeated FieldDescriptorProto extension = 6;
repeated DescriptorProto nested_type = 3;
repeated EnumDescriptorProto enum_type = 4;
message ExtensionRange {
optional int32 start = 1;
optional int32 end = 2;
}
repeated ExtensionRange extension_range = 5;
optional MessageOptions options = 7;
}
// Describes a field within a message.
message FieldDescriptorProto {
enum Type {
// 0 is reserved for errors.
// Order is weird for historical reasons.
TYPE_DOUBLE = 1;
TYPE_FLOAT = 2;
TYPE_INT64 = 3; // Not ZigZag encoded. Negative numbers
// take 10 bytes. Use TYPE_SINT64 if negative
// values are likely.
TYPE_UINT64 = 4;
TYPE_INT32 = 5; // Not ZigZag encoded. Negative numbers
// take 10 bytes. Use TYPE_SINT32 if negative
// values are likely.
TYPE_FIXED64 = 6;
TYPE_FIXED32 = 7;
TYPE_BOOL = 8;
TYPE_STRING = 9;
TYPE_GROUP = 10; // Tag-delimited aggregate.
TYPE_MESSAGE = 11; // Length-delimited aggregate.
// New in version 2.
TYPE_BYTES = 12;
TYPE_UINT32 = 13;
TYPE_ENUM = 14;
TYPE_SFIXED32 = 15;
TYPE_SFIXED64 = 16;
TYPE_SINT32 = 17; // Uses ZigZag encoding.
TYPE_SINT64 = 18; // Uses ZigZag encoding.
};
enum Label {
// 0 is reserved for errors
LABEL_OPTIONAL = 1;
LABEL_REQUIRED = 2;
LABEL_REPEATED = 3;
// TODO(sanjay): Should we add LABEL_MAP?
};
optional string name = 1;
optional int32 number = 3;
optional Label label = 4;
// If type_name is set, this need not be set. If both this and type_name
// are set, this must be either TYPE_ENUM or TYPE_MESSAGE.
optional Type type = 5;
// For message and enum types, this is the name of the type. If the name
// starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
// rules are used to find the type (i.e. first the nested types within this
// message are searched, then within the parent, on up to the root
// namespace).
optional string type_name = 6;
// For extensions, this is the name of the type being extended. It is
// resolved in the same manner as type_name.
optional string extendee = 2;
// For numeric types, contains the original text representation of the value.
// For booleans, "true" or "false".
// For strings, contains the default text contents (not escaped in any way).
// For bytes, contains the C escaped value. All bytes >= 128 are escaped.
// TODO(kenton): Base-64 encode?
optional string default_value = 7;
optional FieldOptions options = 8;
}
// Describes an enum type.
message EnumDescriptorProto {
optional string name = 1;
repeated EnumValueDescriptorProto value = 2;
optional EnumOptions options = 3;
}
// Describes a value within an enum.
message EnumValueDescriptorProto {
optional string name = 1;
optional int32 number = 2;
optional EnumValueOptions options = 3;
}
// Describes a service.
message ServiceDescriptorProto {
optional string name = 1;
repeated MethodDescriptorProto method = 2;
optional ServiceOptions options = 3;
}
// Describes a method of a service.
message MethodDescriptorProto {
optional string name = 1;
// Input and output type names. These are resolved in the same way as
// FieldDescriptorProto.type_name, but must refer to a message type.
optional string input_type = 2;
optional string output_type = 3;
optional MethodOptions options = 4;
}
// ===================================================================
// Options
// Each of the definitions above may have "options" attached. These are
// just annotations which may cause code to be generated slightly differently
// or may contain hints for code that manipulates protocol messages.
//
// Clients may define custom options as extensions of the *Options messages.
// These extensions may not yet be known at parsing time, so the parser cannot
// store the values in them. Instead it stores them in a field in the *Options
// message called uninterpreted_option. This field must have the same name
// across all *Options messages. We then use this field to populate the
// extensions when we build a descriptor, at which point all protos have been
// parsed and so all extensions are known.
//
// Extension numbers for custom options may be chosen as follows:
// * For options which will only be used within a single application or
// organization, or for experimental options, use field numbers 50000
// through 99999. It is up to you to ensure that you do not use the
// same number for multiple options.
// * For options which will be published and used publicly by multiple
// independent entities, e-mail kenton@google.com to reserve extension
// numbers. Simply tell me how many you need and I'll send you back a
// set of numbers to use -- there's no need to explain how you intend to
// use them. If this turns out to be popular, a web service will be set up
// to automatically assign option numbers.
message FileOptions {
// Sets the Java package where classes generated from this .proto will be
// placed. By default, the proto package is used, but this is often
// inappropriate because proto packages do not normally start with backwards
// domain names.
optional string java_package = 1;
// If set, all the classes from the .proto file are wrapped in a single
// outer class with the given name. This applies to both Proto1
// (equivalent to the old "--one_java_file" option) and Proto2 (where
// a .proto always translates to a single class, but you may want to
// explicitly choose the class name).
optional string java_outer_classname = 8;
// If set true, then the Java code generator will generate a separate .java
// file for each top-level message, enum, and service defined in the .proto
// file. Thus, these types will *not* be nested inside the outer class
// named by java_outer_classname. However, the outer class will still be
// generated to contain the file's getDescriptor() method as well as any
// top-level extensions defined in the file.
optional bool java_multiple_files = 10 [default=false];
// Generated classes can be optimized for speed or code size.
enum OptimizeMode {
SPEED = 1; // Generate complete code for parsing, serialization,
// etc.
CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
}
optional OptimizeMode optimize_for = 9 [default=SPEED];
// Should generic services be generated in each language? "Generic" services
// are not specific to any particular RPC system. They are generated by the
// main code generators in each language (without additional plugins).
// Generic services were the only kind of service generation supported by
// early versions of proto2.
//
// Generic services are now considered deprecated in favor of using plugins
// that generate code specific to your particular RPC system. If you are
// using such a plugin, set these to false. In the future, we may change
// the default to false, so if you explicitly want generic services, you
// should explicitly set these to true.
optional bool cc_generic_services = 16 [default=true];
optional bool java_generic_services = 17 [default=true];
optional bool py_generic_services = 18 [default=true];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message MessageOptions {
// Set true to use the old proto1 MessageSet wire format for extensions.
// This is provided for backwards-compatibility with the MessageSet wire
// format. You should not use this for any other reason: It's less
// efficient, has fewer features, and is more complicated.
//
// The message must be defined exactly as follows:
// message Foo {
// option message_set_wire_format = true;
// extensions 4 to max;
// }
// Note that the message cannot have any defined fields; MessageSets only
// have extensions.
//
// All extensions of your type must be singular messages; e.g. they cannot
// be int32s, enums, or repeated messages.
//
// Because this is an option, the above two restrictions are not enforced by
// the protocol compiler.
optional bool message_set_wire_format = 1 [default=false];
// Disables the generation of the standard "descriptor()" accessor, which can
// conflict with a field of the same name. This is meant to make migration
// from proto1 easier; new code should avoid fields named "descriptor".
optional bool no_standard_descriptor_accessor = 2 [default=false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message FieldOptions {
// The ctype option instructs the C++ code generator to use a different
// representation of the field than it normally would. See the specific
// options below. This option is not yet implemented in the open source
// release -- sorry, we'll try to include it in a future version!
optional CType ctype = 1 [default = STRING];
enum CType {
// Default mode.
STRING = 0;
CORD = 1;
STRING_PIECE = 2;
}
// The packed option can be enabled for repeated primitive fields to enable
// a more efficient representation on the wire. Rather than repeatedly
// writing the tag and type for each element, the entire array is encoded as
// a single length-delimited blob.
optional bool packed = 2;
// Is this field deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for accessors, or it will be completely ignored; in the very least, this
// is a formalization for deprecating fields.
optional bool deprecated = 3 [default=false];
// EXPERIMENTAL. DO NOT USE.
// For "map" fields, the name of the field in the enclosed type that
// is the key for this map. For example, suppose we have:
// message Item {
// required string name = 1;
// required string value = 2;
// }
// message Config {
// repeated Item items = 1 [experimental_map_key="name"];
// }
// In this situation, the map key for Item will be set to "name".
// TODO: Fully-implement this, then remove the "experimental_" prefix.
optional string experimental_map_key = 9;
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumOptions {
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumValueOptions {
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message ServiceOptions {
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message MethodOptions {
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
// A message representing a option the parser does not recognize. This only
// appears in options protos created by the compiler::Parser class.
// DescriptorPool resolves these when building Descriptor objects. Therefore,
// options protos in descriptor objects (e.g. returned by Descriptor::options(),
// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
// in them.
message UninterpretedOption {
// The name of the uninterpreted option. Each string represents a segment in
// a dot-separated name. is_extension is true iff a segment represents an
// extension (denoted with parentheses in options specs in .proto files).
// E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
// "foo.(bar.baz).qux".
message NamePart {
required string name_part = 1;
required bool is_extension = 2;
}
repeated NamePart name = 2;
// The value of the uninterpreted option, in whatever type the tokenizer
// identified it as during parsing. Exactly one of these should be set.
optional string identifier_value = 3;
optional uint64 positive_int_value = 4;
optional int64 negative_int_value = 5;
optional double double_value = 6;
optional bytes string_value = 7;
}

View file

@ -0,0 +1,541 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/descriptor_database.h>
#include <set>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/wire_format_lite_inl.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/stl_util-inl.h>
#include <google/protobuf/stubs/map-util.h>
namespace google {
namespace protobuf {
DescriptorDatabase::~DescriptorDatabase() {}
// ===================================================================
template <typename Value>
bool SimpleDescriptorDatabase::DescriptorIndex<Value>::AddFile(
const FileDescriptorProto& file,
Value value) {
if (!InsertIfNotPresent(&by_name_, file.name(), value)) {
GOOGLE_LOG(ERROR) << "File already exists in database: " << file.name();
return false;
}
// We must be careful here -- calling file.package() if file.has_package() is
// false could access an uninitialized static-storage variable if we are being
// run at startup time.
string path = file.has_package() ? file.package() : string();
if (!path.empty()) path += '.';
for (int i = 0; i < file.message_type_size(); i++) {
if (!AddSymbol(path + file.message_type(i).name(), value)) return false;
if (!AddNestedExtensions(file.message_type(i), value)) return false;
}
for (int i = 0; i < file.enum_type_size(); i++) {
if (!AddSymbol(path + file.enum_type(i).name(), value)) return false;
}
for (int i = 0; i < file.extension_size(); i++) {
if (!AddSymbol(path + file.extension(i).name(), value)) return false;
if (!AddExtension(file.extension(i), value)) return false;
}
for (int i = 0; i < file.service_size(); i++) {
if (!AddSymbol(path + file.service(i).name(), value)) return false;
}
return true;
}
template <typename Value>
bool SimpleDescriptorDatabase::DescriptorIndex<Value>::AddSymbol(
const string& name, Value value) {
// We need to make sure not to violate our map invariant.
// If the symbol name is invalid it could break our lookup algorithm (which
// relies on the fact that '.' sorts before all other characters that are
// valid in symbol names).
if (!ValidateSymbolName(name)) {
GOOGLE_LOG(ERROR) << "Invalid symbol name: " << name;
return false;
}
// Try to look up the symbol to make sure a super-symbol doesn't already
// exist.
typename map<string, Value>::iterator iter = FindLastLessOrEqual(name);
if (iter == by_symbol_.end()) {
// Apparently the map is currently empty. Just insert and be done with it.
by_symbol_.insert(make_pair(name, value));
return true;
}
if (IsSubSymbol(iter->first, name)) {
GOOGLE_LOG(ERROR) << "Symbol name \"" << name << "\" conflicts with the existing "
"symbol \"" << iter->first << "\".";
return false;
}
// OK, that worked. Now we have to make sure that no symbol in the map is
// a sub-symbol of the one we are inserting. The only symbol which could
// be so is the first symbol that is greater than the new symbol. Since
// |iter| points at the last symbol that is less than or equal, we just have
// to increment it.
++iter;
if (iter != by_symbol_.end() && IsSubSymbol(name, iter->first)) {
GOOGLE_LOG(ERROR) << "Symbol name \"" << name << "\" conflicts with the existing "
"symbol \"" << iter->first << "\".";
return false;
}
// OK, no conflicts.
// Insert the new symbol using the iterator as a hint, the new entry will
// appear immediately before the one the iterator is pointing at.
by_symbol_.insert(iter, make_pair(name, value));
return true;
}
template <typename Value>
bool SimpleDescriptorDatabase::DescriptorIndex<Value>::AddNestedExtensions(
const DescriptorProto& message_type,
Value value) {
for (int i = 0; i < message_type.nested_type_size(); i++) {
if (!AddNestedExtensions(message_type.nested_type(i), value)) return false;
}
for (int i = 0; i < message_type.extension_size(); i++) {
if (!AddExtension(message_type.extension(i), value)) return false;
}
return true;
}
template <typename Value>
bool SimpleDescriptorDatabase::DescriptorIndex<Value>::AddExtension(
const FieldDescriptorProto& field,
Value value) {
if (!field.extendee().empty() && field.extendee()[0] == '.') {
// The extension is fully-qualified. We can use it as a lookup key in
// the by_symbol_ table.
if (!InsertIfNotPresent(&by_extension_,
make_pair(field.extendee().substr(1),
field.number()),
value)) {
GOOGLE_LOG(ERROR) << "Extension conflicts with extension already in database: "
"extend " << field.extendee() << " { "
<< field.name() << " = " << field.number() << " }";
return false;
}
} else {
// Not fully-qualified. We can't really do anything here, unfortunately.
// We don't consider this an error, though, because the descriptor is
// valid.
}
return true;
}
template <typename Value>
Value SimpleDescriptorDatabase::DescriptorIndex<Value>::FindFile(
const string& filename) {
return FindWithDefault(by_name_, filename, Value());
}
template <typename Value>
Value SimpleDescriptorDatabase::DescriptorIndex<Value>::FindSymbol(
const string& name) {
typename map<string, Value>::iterator iter = FindLastLessOrEqual(name);
return (iter != by_symbol_.end() && IsSubSymbol(iter->first, name)) ?
iter->second : Value();
}
template <typename Value>
Value SimpleDescriptorDatabase::DescriptorIndex<Value>::FindExtension(
const string& containing_type,
int field_number) {
return FindWithDefault(by_extension_,
make_pair(containing_type, field_number),
Value());
}
template <typename Value>
bool SimpleDescriptorDatabase::DescriptorIndex<Value>::FindAllExtensionNumbers(
const string& containing_type,
vector<int>* output) {
typename map<pair<string, int>, Value >::const_iterator it =
by_extension_.lower_bound(make_pair(containing_type, 0));
bool success = false;
for (; it != by_extension_.end() && it->first.first == containing_type;
++it) {
output->push_back(it->first.second);
success = true;
}
return success;
}
template <typename Value>
typename map<string, Value>::iterator
SimpleDescriptorDatabase::DescriptorIndex<Value>::FindLastLessOrEqual(
const string& name) {
// Find the last key in the map which sorts less than or equal to the
// symbol name. Since upper_bound() returns the *first* key that sorts
// *greater* than the input, we want the element immediately before that.
typename map<string, Value>::iterator iter = by_symbol_.upper_bound(name);
if (iter != by_symbol_.begin()) --iter;
return iter;
}
template <typename Value>
bool SimpleDescriptorDatabase::DescriptorIndex<Value>::IsSubSymbol(
const string& sub_symbol, const string& super_symbol) {
return sub_symbol == super_symbol ||
(HasPrefixString(super_symbol, sub_symbol) &&
super_symbol[sub_symbol.size()] == '.');
}
template <typename Value>
bool SimpleDescriptorDatabase::DescriptorIndex<Value>::ValidateSymbolName(
const string& name) {
for (int i = 0; i < name.size(); i++) {
// I don't trust ctype.h due to locales. :(
if (name[i] != '.' && name[i] != '_' &&
(name[i] < '0' || name[i] > '9') &&
(name[i] < 'A' || name[i] > 'Z') &&
(name[i] < 'a' || name[i] > 'z')) {
return false;
}
}
return true;
}
// -------------------------------------------------------------------
SimpleDescriptorDatabase::SimpleDescriptorDatabase() {}
SimpleDescriptorDatabase::~SimpleDescriptorDatabase() {
STLDeleteElements(&files_to_delete_);
}
bool SimpleDescriptorDatabase::Add(const FileDescriptorProto& file) {
FileDescriptorProto* new_file = new FileDescriptorProto;
new_file->CopyFrom(file);
return AddAndOwn(new_file);
}
bool SimpleDescriptorDatabase::AddAndOwn(const FileDescriptorProto* file) {
files_to_delete_.push_back(file);
return index_.AddFile(*file, file);
}
bool SimpleDescriptorDatabase::FindFileByName(
const string& filename,
FileDescriptorProto* output) {
return MaybeCopy(index_.FindFile(filename), output);
}
bool SimpleDescriptorDatabase::FindFileContainingSymbol(
const string& symbol_name,
FileDescriptorProto* output) {
return MaybeCopy(index_.FindSymbol(symbol_name), output);
}
bool SimpleDescriptorDatabase::FindFileContainingExtension(
const string& containing_type,
int field_number,
FileDescriptorProto* output) {
return MaybeCopy(index_.FindExtension(containing_type, field_number), output);
}
bool SimpleDescriptorDatabase::FindAllExtensionNumbers(
const string& extendee_type,
vector<int>* output) {
return index_.FindAllExtensionNumbers(extendee_type, output);
}
bool SimpleDescriptorDatabase::MaybeCopy(const FileDescriptorProto* file,
FileDescriptorProto* output) {
if (file == NULL) return false;
output->CopyFrom(*file);
return true;
}
// -------------------------------------------------------------------
EncodedDescriptorDatabase::EncodedDescriptorDatabase() {}
EncodedDescriptorDatabase::~EncodedDescriptorDatabase() {
for (int i = 0; i < files_to_delete_.size(); i++) {
operator delete(files_to_delete_[i]);
}
}
bool EncodedDescriptorDatabase::Add(
const void* encoded_file_descriptor, int size) {
FileDescriptorProto file;
if (file.ParseFromArray(encoded_file_descriptor, size)) {
return index_.AddFile(file, make_pair(encoded_file_descriptor, size));
} else {
GOOGLE_LOG(ERROR) << "Invalid file descriptor data passed to "
"EncodedDescriptorDatabase::Add().";
return false;
}
}
bool EncodedDescriptorDatabase::AddCopy(
const void* encoded_file_descriptor, int size) {
void* copy = operator new(size);
memcpy(copy, encoded_file_descriptor, size);
files_to_delete_.push_back(copy);
return Add(copy, size);
}
bool EncodedDescriptorDatabase::FindFileByName(
const string& filename,
FileDescriptorProto* output) {
return MaybeParse(index_.FindFile(filename), output);
}
bool EncodedDescriptorDatabase::FindFileContainingSymbol(
const string& symbol_name,
FileDescriptorProto* output) {
return MaybeParse(index_.FindSymbol(symbol_name), output);
}
bool EncodedDescriptorDatabase::FindNameOfFileContainingSymbol(
const string& symbol_name,
string* output) {
pair<const void*, int> encoded_file = index_.FindSymbol(symbol_name);
if (encoded_file.first == NULL) return false;
// Optimization: The name should be the first field in the encoded message.
// Try to just read it directly.
io::CodedInputStream input(reinterpret_cast<const uint8*>(encoded_file.first),
encoded_file.second);
const uint32 kNameTag = internal::WireFormatLite::MakeTag(
FileDescriptorProto::kNameFieldNumber,
internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
if (input.ReadTag() == kNameTag) {
// Success!
return internal::WireFormatLite::ReadString(&input, output);
} else {
// Slow path. Parse whole message.
FileDescriptorProto file_proto;
if (!file_proto.ParseFromArray(encoded_file.first, encoded_file.second)) {
return false;
}
*output = file_proto.name();
return true;
}
}
bool EncodedDescriptorDatabase::FindFileContainingExtension(
const string& containing_type,
int field_number,
FileDescriptorProto* output) {
return MaybeParse(index_.FindExtension(containing_type, field_number),
output);
}
bool EncodedDescriptorDatabase::FindAllExtensionNumbers(
const string& extendee_type,
vector<int>* output) {
return index_.FindAllExtensionNumbers(extendee_type, output);
}
bool EncodedDescriptorDatabase::MaybeParse(
pair<const void*, int> encoded_file,
FileDescriptorProto* output) {
if (encoded_file.first == NULL) return false;
return output->ParseFromArray(encoded_file.first, encoded_file.second);
}
// ===================================================================
DescriptorPoolDatabase::DescriptorPoolDatabase(const DescriptorPool& pool)
: pool_(pool) {}
DescriptorPoolDatabase::~DescriptorPoolDatabase() {}
bool DescriptorPoolDatabase::FindFileByName(
const string& filename,
FileDescriptorProto* output) {
const FileDescriptor* file = pool_.FindFileByName(filename);
if (file == NULL) return false;
output->Clear();
file->CopyTo(output);
return true;
}
bool DescriptorPoolDatabase::FindFileContainingSymbol(
const string& symbol_name,
FileDescriptorProto* output) {
const FileDescriptor* file = pool_.FindFileContainingSymbol(symbol_name);
if (file == NULL) return false;
output->Clear();
file->CopyTo(output);
return true;
}
bool DescriptorPoolDatabase::FindFileContainingExtension(
const string& containing_type,
int field_number,
FileDescriptorProto* output) {
const Descriptor* extendee = pool_.FindMessageTypeByName(containing_type);
if (extendee == NULL) return false;
const FieldDescriptor* extension =
pool_.FindExtensionByNumber(extendee, field_number);
if (extension == NULL) return false;
output->Clear();
extension->file()->CopyTo(output);
return true;
}
bool DescriptorPoolDatabase::FindAllExtensionNumbers(
const string& extendee_type,
vector<int>* output) {
const Descriptor* extendee = pool_.FindMessageTypeByName(extendee_type);
if (extendee == NULL) return false;
vector<const FieldDescriptor*> extensions;
pool_.FindAllExtensions(extendee, &extensions);
for (int i = 0; i < extensions.size(); ++i) {
output->push_back(extensions[i]->number());
}
return true;
}
// ===================================================================
MergedDescriptorDatabase::MergedDescriptorDatabase(
DescriptorDatabase* source1,
DescriptorDatabase* source2) {
sources_.push_back(source1);
sources_.push_back(source2);
}
MergedDescriptorDatabase::MergedDescriptorDatabase(
const vector<DescriptorDatabase*>& sources)
: sources_(sources) {}
MergedDescriptorDatabase::~MergedDescriptorDatabase() {}
bool MergedDescriptorDatabase::FindFileByName(
const string& filename,
FileDescriptorProto* output) {
for (int i = 0; i < sources_.size(); i++) {
if (sources_[i]->FindFileByName(filename, output)) {
return true;
}
}
return false;
}
bool MergedDescriptorDatabase::FindFileContainingSymbol(
const string& symbol_name,
FileDescriptorProto* output) {
for (int i = 0; i < sources_.size(); i++) {
if (sources_[i]->FindFileContainingSymbol(symbol_name, output)) {
// The symbol was found in source i. However, if one of the previous
// sources defines a file with the same name (which presumably doesn't
// contain the symbol, since it wasn't found in that source), then we
// must hide it from the caller.
FileDescriptorProto temp;
for (int j = 0; j < i; j++) {
if (sources_[j]->FindFileByName(output->name(), &temp)) {
// Found conflicting file in a previous source.
return false;
}
}
return true;
}
}
return false;
}
bool MergedDescriptorDatabase::FindFileContainingExtension(
const string& containing_type,
int field_number,
FileDescriptorProto* output) {
for (int i = 0; i < sources_.size(); i++) {
if (sources_[i]->FindFileContainingExtension(
containing_type, field_number, output)) {
// The symbol was found in source i. However, if one of the previous
// sources defines a file with the same name (which presumably doesn't
// contain the symbol, since it wasn't found in that source), then we
// must hide it from the caller.
FileDescriptorProto temp;
for (int j = 0; j < i; j++) {
if (sources_[j]->FindFileByName(output->name(), &temp)) {
// Found conflicting file in a previous source.
return false;
}
}
return true;
}
}
return false;
}
bool MergedDescriptorDatabase::FindAllExtensionNumbers(
const string& extendee_type,
vector<int>* output) {
set<int> merged_results;
vector<int> results;
bool success = false;
for (int i = 0; i < sources_.size(); i++) {
if (sources_[i]->FindAllExtensionNumbers(extendee_type, &results)) {
copy(results.begin(), results.end(),
insert_iterator<set<int> >(merged_results, merged_results.begin()));
success = true;
}
results.clear();
}
copy(merged_results.begin(), merged_results.end(),
insert_iterator<vector<int> >(*output, output->end()));
return success;
}
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,366 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Interface for manipulating databases of descriptors.
#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__
#define GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <google/protobuf/descriptor.h>
namespace google {
namespace protobuf {
// Defined in this file.
class DescriptorDatabase;
class SimpleDescriptorDatabase;
class EncodedDescriptorDatabase;
class DescriptorPoolDatabase;
class MergedDescriptorDatabase;
// Abstract interface for a database of descriptors.
//
// This is useful if you want to create a DescriptorPool which loads
// descriptors on-demand from some sort of large database. If the database
// is large, it may be inefficient to enumerate every .proto file inside it
// calling DescriptorPool::BuildFile() for each one. Instead, a DescriptorPool
// can be created which wraps a DescriptorDatabase and only builds particular
// descriptors when they are needed.
class LIBPROTOBUF_EXPORT DescriptorDatabase {
public:
inline DescriptorDatabase() {}
virtual ~DescriptorDatabase();
// Find a file by file name. Fills in in *output and returns true if found.
// Otherwise, returns false, leaving the contents of *output undefined.
virtual bool FindFileByName(const string& filename,
FileDescriptorProto* output) = 0;
// Find the file that declares the given fully-qualified symbol name.
// If found, fills in *output and returns true, otherwise returns false
// and leaves *output undefined.
virtual bool FindFileContainingSymbol(const string& symbol_name,
FileDescriptorProto* output) = 0;
// Find the file which defines an extension extending the given message type
// with the given field number. If found, fills in *output and returns true,
// otherwise returns false and leaves *output undefined. containing_type
// must be a fully-qualified type name.
virtual bool FindFileContainingExtension(const string& containing_type,
int field_number,
FileDescriptorProto* output) = 0;
// Finds the tag numbers used by all known extensions of
// extendee_type, and appends them to output in an undefined
// order. This method is best-effort: it's not guaranteed that the
// database will find all extensions, and it's not guaranteed that
// FindFileContainingExtension will return true on all of the found
// numbers. Returns true if the search was successful, otherwise
// returns false and leaves output unchanged.
//
// This method has a default implementation that always returns
// false.
virtual bool FindAllExtensionNumbers(const string& extendee_type,
vector<int>* output) {
return false;
}
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorDatabase);
};
// A DescriptorDatabase into which you can insert files manually.
//
// FindFileContainingSymbol() is fully-implemented. When you add a file, its
// symbols will be indexed for this purpose. Note that the implementation
// may return false positives, but only if it isn't possible for the symbol
// to be defined in any other file. In particular, if a file defines a symbol
// "Foo", then searching for "Foo.[anything]" will match that file. This way,
// the database does not need to aggressively index all children of a symbol.
//
// FindFileContainingExtension() is mostly-implemented. It works if and only
// if the original FieldDescriptorProto defining the extension has a
// fully-qualified type name in its "extendee" field (i.e. starts with a '.').
// If the extendee is a relative name, SimpleDescriptorDatabase will not
// attempt to resolve the type, so it will not know what type the extension is
// extending. Therefore, calling FindFileContainingExtension() with the
// extension's containing type will never actually find that extension. Note
// that this is an unlikely problem, as all FileDescriptorProtos created by the
// protocol compiler (as well as ones created by calling
// FileDescriptor::CopyTo()) will always use fully-qualified names for all
// types. You only need to worry if you are constructing FileDescriptorProtos
// yourself, or are calling compiler::Parser directly.
class LIBPROTOBUF_EXPORT SimpleDescriptorDatabase : public DescriptorDatabase {
public:
SimpleDescriptorDatabase();
~SimpleDescriptorDatabase();
// Adds the FileDescriptorProto to the database, making a copy. The object
// can be deleted after Add() returns. Returns false if the file conflicted
// with a file already in the database, in which case an error will have
// been written to GOOGLE_LOG(ERROR).
bool Add(const FileDescriptorProto& file);
// Adds the FileDescriptorProto to the database and takes ownership of it.
bool AddAndOwn(const FileDescriptorProto* file);
// implements DescriptorDatabase -----------------------------------
bool FindFileByName(const string& filename,
FileDescriptorProto* output);
bool FindFileContainingSymbol(const string& symbol_name,
FileDescriptorProto* output);
bool FindFileContainingExtension(const string& containing_type,
int field_number,
FileDescriptorProto* output);
bool FindAllExtensionNumbers(const string& extendee_type,
vector<int>* output);
private:
// So that it can use DescriptorIndex.
friend class EncodedDescriptorDatabase;
// An index mapping file names, symbol names, and extension numbers to
// some sort of values.
template <typename Value>
class DescriptorIndex {
public:
// Helpers to recursively add particular descriptors and all their contents
// to the index.
bool AddFile(const FileDescriptorProto& file,
Value value);
bool AddSymbol(const string& name, Value value);
bool AddNestedExtensions(const DescriptorProto& message_type,
Value value);
bool AddExtension(const FieldDescriptorProto& field,
Value value);
Value FindFile(const string& filename);
Value FindSymbol(const string& name);
Value FindExtension(const string& containing_type, int field_number);
bool FindAllExtensionNumbers(const string& containing_type,
vector<int>* output);
private:
map<string, Value> by_name_;
map<string, Value> by_symbol_;
map<pair<string, int>, Value> by_extension_;
// Invariant: The by_symbol_ map does not contain any symbols which are
// prefixes of other symbols in the map. For example, "foo.bar" is a
// prefix of "foo.bar.baz" (but is not a prefix of "foo.barbaz").
//
// This invariant is important because it means that given a symbol name,
// we can find a key in the map which is a prefix of the symbol in O(lg n)
// time, and we know that there is at most one such key.
//
// The prefix lookup algorithm works like so:
// 1) Find the last key in the map which is less than or equal to the
// search key.
// 2) If the found key is a prefix of the search key, then return it.
// Otherwise, there is no match.
//
// I am sure this algorithm has been described elsewhere, but since I
// wasn't able to find it quickly I will instead prove that it works
// myself. The key to the algorithm is that if a match exists, step (1)
// will find it. Proof:
// 1) Define the "search key" to be the key we are looking for, the "found
// key" to be the key found in step (1), and the "match key" to be the
// key which actually matches the serach key (i.e. the key we're trying
// to find).
// 2) The found key must be less than or equal to the search key by
// definition.
// 3) The match key must also be less than or equal to the search key
// (because it is a prefix).
// 4) The match key cannot be greater than the found key, because if it
// were, then step (1) of the algorithm would have returned the match
// key instead (since it finds the *greatest* key which is less than or
// equal to the search key).
// 5) Therefore, the found key must be between the match key and the search
// key, inclusive.
// 6) Since the search key must be a sub-symbol of the match key, if it is
// not equal to the match key, then search_key[match_key.size()] must
// be '.'.
// 7) Since '.' sorts before any other character that is valid in a symbol
// name, then if the found key is not equal to the match key, then
// found_key[match_key.size()] must also be '.', because any other value
// would make it sort after the search key.
// 8) Therefore, if the found key is not equal to the match key, then the
// found key must be a sub-symbol of the match key. However, this would
// contradict our map invariant which says that no symbol in the map is
// a sub-symbol of any other.
// 9) Therefore, the found key must match the match key.
//
// The above proof assumes the match key exists. In the case that the
// match key does not exist, then step (1) will return some other symbol.
// That symbol cannot be a super-symbol of the search key since if it were,
// then it would be a match, and we're assuming the match key doesn't exist.
// Therefore, step 2 will correctly return no match.
// Find the last entry in the by_symbol_ map whose key is less than or
// equal to the given name.
typename map<string, Value>::iterator FindLastLessOrEqual(
const string& name);
// True if either the arguments are equal or super_symbol identifies a
// parent symbol of sub_symbol (e.g. "foo.bar" is a parent of
// "foo.bar.baz", but not a parent of "foo.barbaz").
bool IsSubSymbol(const string& sub_symbol, const string& super_symbol);
// Returns true if and only if all characters in the name are alphanumerics,
// underscores, or periods.
bool ValidateSymbolName(const string& name);
};
DescriptorIndex<const FileDescriptorProto*> index_;
vector<const FileDescriptorProto*> files_to_delete_;
// If file is non-NULL, copy it into *output and return true, otherwise
// return false.
bool MaybeCopy(const FileDescriptorProto* file,
FileDescriptorProto* output);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SimpleDescriptorDatabase);
};
// Very similar to SimpleDescriptorDatabase, but stores all the descriptors
// as raw bytes and generally tries to use as little memory as possible.
//
// The same caveats regarding FindFileContainingExtension() apply as with
// SimpleDescriptorDatabase.
class LIBPROTOBUF_EXPORT EncodedDescriptorDatabase : public DescriptorDatabase {
public:
EncodedDescriptorDatabase();
~EncodedDescriptorDatabase();
// Adds the FileDescriptorProto to the database. The descriptor is provided
// in encoded form. The database does not make a copy of the bytes, nor
// does it take ownership; it's up to the caller to make sure the bytes
// remain valid for the life of the database. Returns false and logs an error
// if the bytes are not a valid FileDescriptorProto or if the file conflicted
// with a file already in the database.
bool Add(const void* encoded_file_descriptor, int size);
// Like Add(), but makes a copy of the data, so that the caller does not
// need to keep it around.
bool AddCopy(const void* encoded_file_descriptor, int size);
// Like FindFileContainingSymbol but returns only the name of the file.
bool FindNameOfFileContainingSymbol(const string& symbol_name,
string* output);
// implements DescriptorDatabase -----------------------------------
bool FindFileByName(const string& filename,
FileDescriptorProto* output);
bool FindFileContainingSymbol(const string& symbol_name,
FileDescriptorProto* output);
bool FindFileContainingExtension(const string& containing_type,
int field_number,
FileDescriptorProto* output);
bool FindAllExtensionNumbers(const string& extendee_type,
vector<int>* output);
private:
SimpleDescriptorDatabase::DescriptorIndex<pair<const void*, int> > index_;
vector<void*> files_to_delete_;
// If encoded_file.first is non-NULL, parse the data into *output and return
// true, otherwise return false.
bool MaybeParse(pair<const void*, int> encoded_file,
FileDescriptorProto* output);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EncodedDescriptorDatabase);
};
// A DescriptorDatabase that fetches files from a given pool.
class LIBPROTOBUF_EXPORT DescriptorPoolDatabase : public DescriptorDatabase {
public:
DescriptorPoolDatabase(const DescriptorPool& pool);
~DescriptorPoolDatabase();
// implements DescriptorDatabase -----------------------------------
bool FindFileByName(const string& filename,
FileDescriptorProto* output);
bool FindFileContainingSymbol(const string& symbol_name,
FileDescriptorProto* output);
bool FindFileContainingExtension(const string& containing_type,
int field_number,
FileDescriptorProto* output);
bool FindAllExtensionNumbers(const string& extendee_type,
vector<int>* output);
private:
const DescriptorPool& pool_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPoolDatabase);
};
// A DescriptorDatabase that wraps two or more others. It first searches the
// first database and, if that fails, tries the second, and so on.
class LIBPROTOBUF_EXPORT MergedDescriptorDatabase : public DescriptorDatabase {
public:
// Merge just two databases. The sources remain property of the caller.
MergedDescriptorDatabase(DescriptorDatabase* source1,
DescriptorDatabase* source2);
// Merge more than two databases. The sources remain property of the caller.
// The vector may be deleted after the constructor returns but the
// DescriptorDatabases need to stick around.
MergedDescriptorDatabase(const vector<DescriptorDatabase*>& sources);
~MergedDescriptorDatabase();
// implements DescriptorDatabase -----------------------------------
bool FindFileByName(const string& filename,
FileDescriptorProto* output);
bool FindFileContainingSymbol(const string& symbol_name,
FileDescriptorProto* output);
bool FindFileContainingExtension(const string& containing_type,
int field_number,
FileDescriptorProto* output);
// Merges the results of calling all databases. Returns true iff any
// of the databases returned true.
bool FindAllExtensionNumbers(const string& extendee_type,
vector<int>* output);
private:
vector<DescriptorDatabase*> sources_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MergedDescriptorDatabase);
};
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__

View file

@ -0,0 +1,748 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This file makes extensive use of RFC 3092. :)
#include <algorithm>
#include <google/protobuf/descriptor_database.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace {
static void AddToDatabase(SimpleDescriptorDatabase* database,
const char* file_text) {
FileDescriptorProto file_proto;
EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto));
database->Add(file_proto);
}
static void ExpectContainsType(const FileDescriptorProto& proto,
const string& type_name) {
for (int i = 0; i < proto.message_type_size(); i++) {
if (proto.message_type(i).name() == type_name) return;
}
ADD_FAILURE() << "\"" << proto.name()
<< "\" did not contain expected type \""
<< type_name << "\".";
}
// ===================================================================
#if GTEST_HAS_PARAM_TEST
// SimpleDescriptorDatabase, EncodedDescriptorDatabase, and
// DescriptorPoolDatabase call for very similar tests. Instead of writing
// three nearly-identical sets of tests, we use parameterized tests to apply
// the same code to all three.
// The parameterized test runs against a DescriptarDatabaseTestCase. We have
// implementations for each of the three classes we want to test.
class DescriptorDatabaseTestCase {
public:
virtual ~DescriptorDatabaseTestCase() {}
virtual DescriptorDatabase* GetDatabase() = 0;
virtual bool AddToDatabase(const FileDescriptorProto& file) = 0;
};
// Factory function type.
typedef DescriptorDatabaseTestCase* DescriptorDatabaseTestCaseFactory();
// Specialization for SimpleDescriptorDatabase.
class SimpleDescriptorDatabaseTestCase : public DescriptorDatabaseTestCase {
public:
static DescriptorDatabaseTestCase* New() {
return new SimpleDescriptorDatabaseTestCase;
}
virtual ~SimpleDescriptorDatabaseTestCase() {}
virtual DescriptorDatabase* GetDatabase() {
return &database_;
}
virtual bool AddToDatabase(const FileDescriptorProto& file) {
return database_.Add(file);
}
private:
SimpleDescriptorDatabase database_;
};
// Specialization for EncodedDescriptorDatabase.
class EncodedDescriptorDatabaseTestCase : public DescriptorDatabaseTestCase {
public:
static DescriptorDatabaseTestCase* New() {
return new EncodedDescriptorDatabaseTestCase;
}
virtual ~EncodedDescriptorDatabaseTestCase() {}
virtual DescriptorDatabase* GetDatabase() {
return &database_;
}
virtual bool AddToDatabase(const FileDescriptorProto& file) {
string data;
file.SerializeToString(&data);
return database_.AddCopy(data.data(), data.size());
}
private:
EncodedDescriptorDatabase database_;
};
// Specialization for DescriptorPoolDatabase.
class DescriptorPoolDatabaseTestCase : public DescriptorDatabaseTestCase {
public:
static DescriptorDatabaseTestCase* New() {
return new EncodedDescriptorDatabaseTestCase;
}
DescriptorPoolDatabaseTestCase() : database_(pool_) {}
virtual ~DescriptorPoolDatabaseTestCase() {}
virtual DescriptorDatabase* GetDatabase() {
return &database_;
}
virtual bool AddToDatabase(const FileDescriptorProto& file) {
return pool_.BuildFile(file);
}
private:
DescriptorPool pool_;
DescriptorPoolDatabase database_;
};
// -------------------------------------------------------------------
class DescriptorDatabaseTest
: public testing::TestWithParam<DescriptorDatabaseTestCaseFactory*> {
protected:
virtual void SetUp() {
test_case_.reset(GetParam()());
database_ = test_case_->GetDatabase();
}
void AddToDatabase(const char* file_descriptor_text) {
FileDescriptorProto file_proto;
EXPECT_TRUE(TextFormat::ParseFromString(file_descriptor_text, &file_proto));
EXPECT_TRUE(test_case_->AddToDatabase(file_proto));
}
void AddToDatabaseWithError(const char* file_descriptor_text) {
FileDescriptorProto file_proto;
EXPECT_TRUE(TextFormat::ParseFromString(file_descriptor_text, &file_proto));
EXPECT_FALSE(test_case_->AddToDatabase(file_proto));
}
scoped_ptr<DescriptorDatabaseTestCase> test_case_;
DescriptorDatabase* database_;
};
TEST_P(DescriptorDatabaseTest, FindFileByName) {
AddToDatabase(
"name: \"foo.proto\" "
"message_type { name:\"Foo\" }");
AddToDatabase(
"name: \"bar.proto\" "
"message_type { name:\"Bar\" }");
{
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileByName("foo.proto", &file));
EXPECT_EQ("foo.proto", file.name());
ExpectContainsType(file, "Foo");
}
{
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileByName("bar.proto", &file));
EXPECT_EQ("bar.proto", file.name());
ExpectContainsType(file, "Bar");
}
{
// Fails to find undefined files.
FileDescriptorProto file;
EXPECT_FALSE(database_->FindFileByName("baz.proto", &file));
}
}
TEST_P(DescriptorDatabaseTest, FindFileContainingSymbol) {
AddToDatabase(
"name: \"foo.proto\" "
"message_type { "
" name: \"Foo\" "
" field { name:\"qux\" }"
" nested_type { name: \"Grault\" } "
" enum_type { name: \"Garply\" } "
"} "
"enum_type { "
" name: \"Waldo\" "
" value { name:\"FRED\" } "
"} "
"extension { name: \"plugh\" } "
"service { "
" name: \"Xyzzy\" "
" method { name: \"Thud\" } "
"}"
);
AddToDatabase(
"name: \"bar.proto\" "
"package: \"corge\" "
"message_type { name: \"Bar\" }");
{
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("Foo", &file));
EXPECT_EQ("foo.proto", file.name());
}
{
// Can find fields.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("Foo.qux", &file));
EXPECT_EQ("foo.proto", file.name());
}
{
// Can find nested types.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("Foo.Grault", &file));
EXPECT_EQ("foo.proto", file.name());
}
{
// Can find nested enums.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("Foo.Garply", &file));
EXPECT_EQ("foo.proto", file.name());
}
{
// Can find enum types.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("Waldo", &file));
EXPECT_EQ("foo.proto", file.name());
}
{
// Can find enum values.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("Waldo.FRED", &file));
EXPECT_EQ("foo.proto", file.name());
}
{
// Can find extensions.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("plugh", &file));
EXPECT_EQ("foo.proto", file.name());
}
{
// Can find services.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("Xyzzy", &file));
EXPECT_EQ("foo.proto", file.name());
}
{
// Can find methods.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("Xyzzy.Thud", &file));
EXPECT_EQ("foo.proto", file.name());
}
{
// Can find things in packages.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingSymbol("corge.Bar", &file));
EXPECT_EQ("bar.proto", file.name());
}
{
// Fails to find undefined symbols.
FileDescriptorProto file;
EXPECT_FALSE(database_->FindFileContainingSymbol("Baz", &file));
}
{
// Names must be fully-qualified.
FileDescriptorProto file;
EXPECT_FALSE(database_->FindFileContainingSymbol("Bar", &file));
}
}
TEST_P(DescriptorDatabaseTest, FindFileContainingExtension) {
AddToDatabase(
"name: \"foo.proto\" "
"message_type { "
" name: \"Foo\" "
" extension_range { start: 1 end: 1000 } "
" extension { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 "
" extendee: \".Foo\" }"
"}");
AddToDatabase(
"name: \"bar.proto\" "
"package: \"corge\" "
"dependency: \"foo.proto\" "
"message_type { "
" name: \"Bar\" "
" extension_range { start: 1 end: 1000 } "
"} "
"extension { name:\"grault\" extendee: \".Foo\" number:32 } "
"extension { name:\"garply\" extendee: \".corge.Bar\" number:70 } "
"extension { name:\"waldo\" extendee: \"Bar\" number:56 } ");
{
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingExtension("Foo", 5, &file));
EXPECT_EQ("foo.proto", file.name());
}
{
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingExtension("Foo", 32, &file));
EXPECT_EQ("bar.proto", file.name());
}
{
// Can find extensions for qualified type names.
FileDescriptorProto file;
EXPECT_TRUE(database_->FindFileContainingExtension("corge.Bar", 70, &file));
EXPECT_EQ("bar.proto", file.name());
}
{
// Can't find extensions whose extendee was not fully-qualified in the
// FileDescriptorProto.
FileDescriptorProto file;
EXPECT_FALSE(database_->FindFileContainingExtension("Bar", 56, &file));
EXPECT_FALSE(
database_->FindFileContainingExtension("corge.Bar", 56, &file));
}
{
// Can't find non-existent extension numbers.
FileDescriptorProto file;
EXPECT_FALSE(database_->FindFileContainingExtension("Foo", 12, &file));
}
{
// Can't find extensions for non-existent types.
FileDescriptorProto file;
EXPECT_FALSE(
database_->FindFileContainingExtension("NoSuchType", 5, &file));
}
{
// Can't find extensions for unqualified type names.
FileDescriptorProto file;
EXPECT_FALSE(database_->FindFileContainingExtension("Bar", 70, &file));
}
}
TEST_P(DescriptorDatabaseTest, FindAllExtensionNumbers) {
AddToDatabase(
"name: \"foo.proto\" "
"message_type { "
" name: \"Foo\" "
" extension_range { start: 1 end: 1000 } "
" extension { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 "
" extendee: \".Foo\" }"
"}");
AddToDatabase(
"name: \"bar.proto\" "
"package: \"corge\" "
"dependency: \"foo.proto\" "
"message_type { "
" name: \"Bar\" "
" extension_range { start: 1 end: 1000 } "
"} "
"extension { name:\"grault\" extendee: \".Foo\" number:32 } "
"extension { name:\"garply\" extendee: \".corge.Bar\" number:70 } "
"extension { name:\"waldo\" extendee: \"Bar\" number:56 } ");
{
vector<int> numbers;
EXPECT_TRUE(database_->FindAllExtensionNumbers("Foo", &numbers));
ASSERT_EQ(2, numbers.size());
sort(numbers.begin(), numbers.end());
EXPECT_EQ(5, numbers[0]);
EXPECT_EQ(32, numbers[1]);
}
{
vector<int> numbers;
EXPECT_TRUE(database_->FindAllExtensionNumbers("corge.Bar", &numbers));
// Note: won't find extension 56 due to the name not being fully qualified.
ASSERT_EQ(1, numbers.size());
EXPECT_EQ(70, numbers[0]);
}
{
// Can't find extensions for non-existent types.
vector<int> numbers;
EXPECT_FALSE(database_->FindAllExtensionNumbers("NoSuchType", &numbers));
}
{
// Can't find extensions for unqualified types.
vector<int> numbers;
EXPECT_FALSE(database_->FindAllExtensionNumbers("Bar", &numbers));
}
}
TEST_P(DescriptorDatabaseTest, ConflictingFileError) {
AddToDatabase(
"name: \"foo.proto\" "
"message_type { "
" name: \"Foo\" "
"}");
AddToDatabaseWithError(
"name: \"foo.proto\" "
"message_type { "
" name: \"Bar\" "
"}");
}
TEST_P(DescriptorDatabaseTest, ConflictingTypeError) {
AddToDatabase(
"name: \"foo.proto\" "
"message_type { "
" name: \"Foo\" "
"}");
AddToDatabaseWithError(
"name: \"bar.proto\" "
"message_type { "
" name: \"Foo\" "
"}");
}
TEST_P(DescriptorDatabaseTest, ConflictingExtensionError) {
AddToDatabase(
"name: \"foo.proto\" "
"extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 "
" extendee: \".Foo\" }");
AddToDatabaseWithError(
"name: \"bar.proto\" "
"extension { name:\"bar\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 "
" extendee: \".Foo\" }");
}
INSTANTIATE_TEST_CASE_P(Simple, DescriptorDatabaseTest,
testing::Values(&SimpleDescriptorDatabaseTestCase::New));
INSTANTIATE_TEST_CASE_P(MemoryConserving, DescriptorDatabaseTest,
testing::Values(&EncodedDescriptorDatabaseTestCase::New));
INSTANTIATE_TEST_CASE_P(Pool, DescriptorDatabaseTest,
testing::Values(&DescriptorPoolDatabaseTestCase::New));
#endif // GTEST_HAS_PARAM_TEST
TEST(EncodedDescriptorDatabaseExtraTest, FindNameOfFileContainingSymbol) {
// Create two files, one of which is in two parts.
FileDescriptorProto file1, file2a, file2b;
file1.set_name("foo.proto");
file1.set_package("foo");
file1.add_message_type()->set_name("Foo");
file2a.set_name("bar.proto");
file2b.set_package("bar");
file2b.add_message_type()->set_name("Bar");
// Normal serialization allows our optimization to kick in.
string data1 = file1.SerializeAsString();
// Force out-of-order serialization to test slow path.
string data2 = file2b.SerializeAsString() + file2a.SerializeAsString();
// Create EncodedDescriptorDatabase containing both files.
EncodedDescriptorDatabase db;
db.Add(data1.data(), data1.size());
db.Add(data2.data(), data2.size());
// Test!
string filename;
EXPECT_TRUE(db.FindNameOfFileContainingSymbol("foo.Foo", &filename));
EXPECT_EQ("foo.proto", filename);
EXPECT_TRUE(db.FindNameOfFileContainingSymbol("foo.Foo.Blah", &filename));
EXPECT_EQ("foo.proto", filename);
EXPECT_TRUE(db.FindNameOfFileContainingSymbol("bar.Bar", &filename));
EXPECT_EQ("bar.proto", filename);
EXPECT_FALSE(db.FindNameOfFileContainingSymbol("foo", &filename));
EXPECT_FALSE(db.FindNameOfFileContainingSymbol("bar", &filename));
EXPECT_FALSE(db.FindNameOfFileContainingSymbol("baz.Baz", &filename));
}
// ===================================================================
class MergedDescriptorDatabaseTest : public testing::Test {
protected:
MergedDescriptorDatabaseTest()
: forward_merged_(&database1_, &database2_),
reverse_merged_(&database2_, &database1_) {}
virtual void SetUp() {
AddToDatabase(&database1_,
"name: \"foo.proto\" "
"message_type { name:\"Foo\" extension_range { start: 1 end: 100 } } "
"extension { name:\"foo_ext\" extendee: \".Foo\" number:3 "
" label:LABEL_OPTIONAL type:TYPE_INT32 } ");
AddToDatabase(&database2_,
"name: \"bar.proto\" "
"message_type { name:\"Bar\" extension_range { start: 1 end: 100 } } "
"extension { name:\"bar_ext\" extendee: \".Bar\" number:5 "
" label:LABEL_OPTIONAL type:TYPE_INT32 } ");
// baz.proto exists in both pools, with different definitions.
AddToDatabase(&database1_,
"name: \"baz.proto\" "
"message_type { name:\"Baz\" extension_range { start: 1 end: 100 } } "
"message_type { name:\"FromPool1\" } "
"extension { name:\"baz_ext\" extendee: \".Baz\" number:12 "
" label:LABEL_OPTIONAL type:TYPE_INT32 } "
"extension { name:\"database1_only_ext\" extendee: \".Baz\" number:13 "
" label:LABEL_OPTIONAL type:TYPE_INT32 } ");
AddToDatabase(&database2_,
"name: \"baz.proto\" "
"message_type { name:\"Baz\" extension_range { start: 1 end: 100 } } "
"message_type { name:\"FromPool2\" } "
"extension { name:\"baz_ext\" extendee: \".Baz\" number:12 "
" label:LABEL_OPTIONAL type:TYPE_INT32 } ");
}
SimpleDescriptorDatabase database1_;
SimpleDescriptorDatabase database2_;
MergedDescriptorDatabase forward_merged_;
MergedDescriptorDatabase reverse_merged_;
};
TEST_F(MergedDescriptorDatabaseTest, FindFileByName) {
{
// Can find file that is only in database1_.
FileDescriptorProto file;
EXPECT_TRUE(forward_merged_.FindFileByName("foo.proto", &file));
EXPECT_EQ("foo.proto", file.name());
ExpectContainsType(file, "Foo");
}
{
// Can find file that is only in database2_.
FileDescriptorProto file;
EXPECT_TRUE(forward_merged_.FindFileByName("bar.proto", &file));
EXPECT_EQ("bar.proto", file.name());
ExpectContainsType(file, "Bar");
}
{
// In forward_merged_, database1_'s baz.proto takes precedence.
FileDescriptorProto file;
EXPECT_TRUE(forward_merged_.FindFileByName("baz.proto", &file));
EXPECT_EQ("baz.proto", file.name());
ExpectContainsType(file, "FromPool1");
}
{
// In reverse_merged_, database2_'s baz.proto takes precedence.
FileDescriptorProto file;
EXPECT_TRUE(reverse_merged_.FindFileByName("baz.proto", &file));
EXPECT_EQ("baz.proto", file.name());
ExpectContainsType(file, "FromPool2");
}
{
// Can't find non-existent file.
FileDescriptorProto file;
EXPECT_FALSE(forward_merged_.FindFileByName("no_such.proto", &file));
}
}
TEST_F(MergedDescriptorDatabaseTest, FindFileContainingSymbol) {
{
// Can find file that is only in database1_.
FileDescriptorProto file;
EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Foo", &file));
EXPECT_EQ("foo.proto", file.name());
ExpectContainsType(file, "Foo");
}
{
// Can find file that is only in database2_.
FileDescriptorProto file;
EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Bar", &file));
EXPECT_EQ("bar.proto", file.name());
ExpectContainsType(file, "Bar");
}
{
// In forward_merged_, database1_'s baz.proto takes precedence.
FileDescriptorProto file;
EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Baz", &file));
EXPECT_EQ("baz.proto", file.name());
ExpectContainsType(file, "FromPool1");
}
{
// In reverse_merged_, database2_'s baz.proto takes precedence.
FileDescriptorProto file;
EXPECT_TRUE(reverse_merged_.FindFileContainingSymbol("Baz", &file));
EXPECT_EQ("baz.proto", file.name());
ExpectContainsType(file, "FromPool2");
}
{
// FromPool1 only shows up in forward_merged_ because it is masked by
// database2_'s baz.proto in reverse_merged_.
FileDescriptorProto file;
EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("FromPool1", &file));
EXPECT_FALSE(reverse_merged_.FindFileContainingSymbol("FromPool1", &file));
}
{
// Can't find non-existent symbol.
FileDescriptorProto file;
EXPECT_FALSE(
forward_merged_.FindFileContainingSymbol("NoSuchType", &file));
}
}
TEST_F(MergedDescriptorDatabaseTest, FindFileContainingExtension) {
{
// Can find file that is only in database1_.
FileDescriptorProto file;
EXPECT_TRUE(
forward_merged_.FindFileContainingExtension("Foo", 3, &file));
EXPECT_EQ("foo.proto", file.name());
ExpectContainsType(file, "Foo");
}
{
// Can find file that is only in database2_.
FileDescriptorProto file;
EXPECT_TRUE(
forward_merged_.FindFileContainingExtension("Bar", 5, &file));
EXPECT_EQ("bar.proto", file.name());
ExpectContainsType(file, "Bar");
}
{
// In forward_merged_, database1_'s baz.proto takes precedence.
FileDescriptorProto file;
EXPECT_TRUE(
forward_merged_.FindFileContainingExtension("Baz", 12, &file));
EXPECT_EQ("baz.proto", file.name());
ExpectContainsType(file, "FromPool1");
}
{
// In reverse_merged_, database2_'s baz.proto takes precedence.
FileDescriptorProto file;
EXPECT_TRUE(
reverse_merged_.FindFileContainingExtension("Baz", 12, &file));
EXPECT_EQ("baz.proto", file.name());
ExpectContainsType(file, "FromPool2");
}
{
// Baz's extension 13 only shows up in forward_merged_ because it is
// masked by database2_'s baz.proto in reverse_merged_.
FileDescriptorProto file;
EXPECT_TRUE(forward_merged_.FindFileContainingExtension("Baz", 13, &file));
EXPECT_FALSE(reverse_merged_.FindFileContainingExtension("Baz", 13, &file));
}
{
// Can't find non-existent extension.
FileDescriptorProto file;
EXPECT_FALSE(
forward_merged_.FindFileContainingExtension("Foo", 6, &file));
}
}
TEST_F(MergedDescriptorDatabaseTest, FindAllExtensionNumbers) {
{
// Message only has extension in database1_
vector<int> numbers;
EXPECT_TRUE(forward_merged_.FindAllExtensionNumbers("Foo", &numbers));
ASSERT_EQ(1, numbers.size());
EXPECT_EQ(3, numbers[0]);
}
{
// Message only has extension in database2_
vector<int> numbers;
EXPECT_TRUE(forward_merged_.FindAllExtensionNumbers("Bar", &numbers));
ASSERT_EQ(1, numbers.size());
EXPECT_EQ(5, numbers[0]);
}
{
// Merge results from the two databases.
vector<int> numbers;
EXPECT_TRUE(forward_merged_.FindAllExtensionNumbers("Baz", &numbers));
ASSERT_EQ(2, numbers.size());
sort(numbers.begin(), numbers.end());
EXPECT_EQ(12, numbers[0]);
EXPECT_EQ(13, numbers[1]);
}
{
vector<int> numbers;
EXPECT_TRUE(reverse_merged_.FindAllExtensionNumbers("Baz", &numbers));
ASSERT_EQ(2, numbers.size());
sort(numbers.begin(), numbers.end());
EXPECT_EQ(12, numbers[0]);
EXPECT_EQ(13, numbers[1]);
}
{
// Can't find extensions for a non-existent message.
vector<int> numbers;
EXPECT_FALSE(reverse_merged_.FindAllExtensionNumbers("Blah", &numbers));
}
}
} // anonymous namespace
} // namespace protobuf
} // namespace google

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,558 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// DynamicMessage is implemented by constructing a data structure which
// has roughly the same memory layout as a generated message would have.
// Then, we use GeneratedMessageReflection to implement our reflection
// interface. All the other operations we need to implement (e.g.
// parsing, copying, etc.) are already implemented in terms of
// Reflection, so the rest is easy.
//
// The up side of this strategy is that it's very efficient. We don't
// need to use hash_maps or generic representations of fields. The
// down side is that this is a low-level memory management hack which
// can be tricky to get right.
//
// As mentioned in the header, we only expose a DynamicMessageFactory
// publicly, not the DynamicMessage class itself. This is because
// GenericMessageReflection wants to have a pointer to a "default"
// copy of the class, with all fields initialized to their default
// values. We only want to construct one of these per message type,
// so DynamicMessageFactory stores a cache of default messages for
// each type it sees (each unique Descriptor pointer). The code
// refers to the "default" copy of the class as the "prototype".
//
// Note on memory allocation: This module often calls "operator new()"
// to allocate untyped memory, rather than calling something like
// "new uint8[]". This is because "operator new()" means "Give me some
// space which I can use as I please." while "new uint8[]" means "Give
// me an array of 8-bit integers.". In practice, the later may return
// a pointer that is not aligned correctly for general use. I believe
// Item 8 of "More Effective C++" discusses this in more detail, though
// I don't have the book on me right now so I'm not sure.
#include <algorithm>
#include <google/protobuf/stubs/hash.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/generated_message_util.h>
#include <google/protobuf/generated_message_reflection.h>
#include <google/protobuf/reflection_ops.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/extension_set.h>
#include <google/protobuf/wire_format.h>
namespace google {
namespace protobuf {
using internal::WireFormat;
using internal::ExtensionSet;
using internal::GeneratedMessageReflection;
// ===================================================================
// Some helper tables and functions...
namespace {
// Compute the byte size of the in-memory representation of the field.
int FieldSpaceUsed(const FieldDescriptor* field) {
typedef FieldDescriptor FD; // avoid line wrapping
if (field->label() == FD::LABEL_REPEATED) {
switch (field->cpp_type()) {
case FD::CPPTYPE_INT32 : return sizeof(RepeatedField<int32 >);
case FD::CPPTYPE_INT64 : return sizeof(RepeatedField<int64 >);
case FD::CPPTYPE_UINT32 : return sizeof(RepeatedField<uint32 >);
case FD::CPPTYPE_UINT64 : return sizeof(RepeatedField<uint64 >);
case FD::CPPTYPE_DOUBLE : return sizeof(RepeatedField<double >);
case FD::CPPTYPE_FLOAT : return sizeof(RepeatedField<float >);
case FD::CPPTYPE_BOOL : return sizeof(RepeatedField<bool >);
case FD::CPPTYPE_ENUM : return sizeof(RepeatedField<int >);
case FD::CPPTYPE_MESSAGE: return sizeof(RepeatedPtrField<Message>);
case FD::CPPTYPE_STRING:
switch (field->options().ctype()) {
default: // TODO(kenton): Support other string reps.
case FieldOptions::STRING:
return sizeof(RepeatedPtrField<string>);
}
break;
}
} else {
switch (field->cpp_type()) {
case FD::CPPTYPE_INT32 : return sizeof(int32 );
case FD::CPPTYPE_INT64 : return sizeof(int64 );
case FD::CPPTYPE_UINT32 : return sizeof(uint32 );
case FD::CPPTYPE_UINT64 : return sizeof(uint64 );
case FD::CPPTYPE_DOUBLE : return sizeof(double );
case FD::CPPTYPE_FLOAT : return sizeof(float );
case FD::CPPTYPE_BOOL : return sizeof(bool );
case FD::CPPTYPE_ENUM : return sizeof(int );
case FD::CPPTYPE_MESSAGE: return sizeof(Message*);
case FD::CPPTYPE_STRING:
switch (field->options().ctype()) {
default: // TODO(kenton): Support other string reps.
case FieldOptions::STRING:
return sizeof(string*);
}
break;
}
}
GOOGLE_LOG(DFATAL) << "Can't get here.";
return 0;
}
inline int DivideRoundingUp(int i, int j) {
return (i + (j - 1)) / j;
}
static const int kSafeAlignment = sizeof(uint64);
inline int AlignTo(int offset, int alignment) {
return DivideRoundingUp(offset, alignment) * alignment;
}
// Rounds the given byte offset up to the next offset aligned such that any
// type may be stored at it.
inline int AlignOffset(int offset) {
return AlignTo(offset, kSafeAlignment);
}
#define bitsizeof(T) (sizeof(T) * 8)
} // namespace
// ===================================================================
class DynamicMessage : public Message {
public:
struct TypeInfo {
int size;
int has_bits_offset;
int unknown_fields_offset;
int extensions_offset;
// Not owned by the TypeInfo.
DynamicMessageFactory* factory; // The factory that created this object.
const DescriptorPool* pool; // The factory's DescriptorPool.
const Descriptor* type; // Type of this DynamicMessage.
// Warning: The order in which the following pointers are defined is
// important (the prototype must be deleted *before* the offsets).
scoped_array<int> offsets;
scoped_ptr<const GeneratedMessageReflection> reflection;
scoped_ptr<const DynamicMessage> prototype;
};
DynamicMessage(const TypeInfo* type_info);
~DynamicMessage();
// Called on the prototype after construction to initialize message fields.
void CrossLinkPrototypes();
// implements Message ----------------------------------------------
Message* New() const;
int GetCachedSize() const;
void SetCachedSize(int size) const;
Metadata GetMetadata() const;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMessage);
inline bool is_prototype() const {
return type_info_->prototype == this ||
// If type_info_->prototype is NULL, then we must be constructing
// the prototype now, which means we must be the prototype.
type_info_->prototype == NULL;
}
inline void* OffsetToPointer(int offset) {
return reinterpret_cast<uint8*>(this) + offset;
}
inline const void* OffsetToPointer(int offset) const {
return reinterpret_cast<const uint8*>(this) + offset;
}
const TypeInfo* type_info_;
// TODO(kenton): Make this an atomic<int> when C++ supports it.
mutable int cached_byte_size_;
};
DynamicMessage::DynamicMessage(const TypeInfo* type_info)
: type_info_(type_info),
cached_byte_size_(0) {
// We need to call constructors for various fields manually and set
// default values where appropriate. We use placement new to call
// constructors. If you haven't heard of placement new, I suggest Googling
// it now. We use placement new even for primitive types that don't have
// constructors for consistency. (In theory, placement new should be used
// any time you are trying to convert untyped memory to typed memory, though
// in practice that's not strictly necessary for types that don't have a
// constructor.)
const Descriptor* descriptor = type_info_->type;
new(OffsetToPointer(type_info_->unknown_fields_offset)) UnknownFieldSet;
if (type_info_->extensions_offset != -1) {
new(OffsetToPointer(type_info_->extensions_offset)) ExtensionSet;
}
for (int i = 0; i < descriptor->field_count(); i++) {
const FieldDescriptor* field = descriptor->field(i);
void* field_ptr = OffsetToPointer(type_info_->offsets[i]);
switch (field->cpp_type()) {
#define HANDLE_TYPE(CPPTYPE, TYPE) \
case FieldDescriptor::CPPTYPE_##CPPTYPE: \
if (!field->is_repeated()) { \
new(field_ptr) TYPE(field->default_value_##TYPE()); \
} else { \
new(field_ptr) RepeatedField<TYPE>(); \
} \
break;
HANDLE_TYPE(INT32 , int32 );
HANDLE_TYPE(INT64 , int64 );
HANDLE_TYPE(UINT32, uint32);
HANDLE_TYPE(UINT64, uint64);
HANDLE_TYPE(DOUBLE, double);
HANDLE_TYPE(FLOAT , float );
HANDLE_TYPE(BOOL , bool );
#undef HANDLE_TYPE
case FieldDescriptor::CPPTYPE_ENUM:
if (!field->is_repeated()) {
new(field_ptr) int(field->default_value_enum()->number());
} else {
new(field_ptr) RepeatedField<int>();
}
break;
case FieldDescriptor::CPPTYPE_STRING:
switch (field->options().ctype()) {
default: // TODO(kenton): Support other string reps.
case FieldOptions::STRING:
if (!field->is_repeated()) {
if (is_prototype()) {
new(field_ptr) const string*(&field->default_value_string());
} else {
string* default_value =
*reinterpret_cast<string* const*>(
type_info_->prototype->OffsetToPointer(
type_info_->offsets[i]));
new(field_ptr) string*(default_value);
}
} else {
new(field_ptr) RepeatedPtrField<string>();
}
break;
}
break;
case FieldDescriptor::CPPTYPE_MESSAGE: {
if (!field->is_repeated()) {
new(field_ptr) Message*(NULL);
} else {
new(field_ptr) RepeatedPtrField<Message>();
}
break;
}
}
}
}
DynamicMessage::~DynamicMessage() {
const Descriptor* descriptor = type_info_->type;
reinterpret_cast<UnknownFieldSet*>(
OffsetToPointer(type_info_->unknown_fields_offset))->~UnknownFieldSet();
if (type_info_->extensions_offset != -1) {
reinterpret_cast<ExtensionSet*>(
OffsetToPointer(type_info_->extensions_offset))->~ExtensionSet();
}
// We need to manually run the destructors for repeated fields and strings,
// just as we ran their constructors in the the DynamicMessage constructor.
// Additionally, if any singular embedded messages have been allocated, we
// need to delete them, UNLESS we are the prototype message of this type,
// in which case any embedded messages are other prototypes and shouldn't
// be touched.
for (int i = 0; i < descriptor->field_count(); i++) {
const FieldDescriptor* field = descriptor->field(i);
void* field_ptr = OffsetToPointer(type_info_->offsets[i]);
if (field->is_repeated()) {
switch (field->cpp_type()) {
#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \
case FieldDescriptor::CPPTYPE_##UPPERCASE : \
reinterpret_cast<RepeatedField<LOWERCASE>*>(field_ptr) \
->~RepeatedField<LOWERCASE>(); \
break
HANDLE_TYPE( INT32, int32);
HANDLE_TYPE( INT64, int64);
HANDLE_TYPE(UINT32, uint32);
HANDLE_TYPE(UINT64, uint64);
HANDLE_TYPE(DOUBLE, double);
HANDLE_TYPE( FLOAT, float);
HANDLE_TYPE( BOOL, bool);
HANDLE_TYPE( ENUM, int);
#undef HANDLE_TYPE
case FieldDescriptor::CPPTYPE_STRING:
switch (field->options().ctype()) {
default: // TODO(kenton): Support other string reps.
case FieldOptions::STRING:
reinterpret_cast<RepeatedPtrField<string>*>(field_ptr)
->~RepeatedPtrField<string>();
break;
}
break;
case FieldDescriptor::CPPTYPE_MESSAGE:
reinterpret_cast<RepeatedPtrField<Message>*>(field_ptr)
->~RepeatedPtrField<Message>();
break;
}
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
switch (field->options().ctype()) {
default: // TODO(kenton): Support other string reps.
case FieldOptions::STRING: {
string* ptr = *reinterpret_cast<string**>(field_ptr);
if (ptr != &field->default_value_string()) {
delete ptr;
}
break;
}
}
} else if ((field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) &&
!is_prototype()) {
Message* message = *reinterpret_cast<Message**>(field_ptr);
if (message != NULL) {
delete message;
}
}
}
}
void DynamicMessage::CrossLinkPrototypes() {
// This should only be called on the prototype message.
GOOGLE_CHECK(is_prototype());
DynamicMessageFactory* factory = type_info_->factory;
const Descriptor* descriptor = type_info_->type;
// Cross-link default messages.
for (int i = 0; i < descriptor->field_count(); i++) {
const FieldDescriptor* field = descriptor->field(i);
void* field_ptr = OffsetToPointer(type_info_->offsets[i]);
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
!field->is_repeated()) {
// For fields with message types, we need to cross-link with the
// prototype for the field's type.
// For singular fields, the field is just a pointer which should
// point to the prototype.
*reinterpret_cast<const Message**>(field_ptr) =
factory->GetPrototypeNoLock(field->message_type());
}
}
}
Message* DynamicMessage::New() const {
void* new_base = reinterpret_cast<uint8*>(operator new(type_info_->size));
memset(new_base, 0, type_info_->size);
return new(new_base) DynamicMessage(type_info_);
}
int DynamicMessage::GetCachedSize() const {
return cached_byte_size_;
}
void DynamicMessage::SetCachedSize(int size) const {
// This is theoretically not thread-compatible, but in practice it works
// because if multiple threads write this simultaneously, they will be
// writing the exact same value.
cached_byte_size_ = size;
}
Metadata DynamicMessage::GetMetadata() const {
Metadata metadata;
metadata.descriptor = type_info_->type;
metadata.reflection = type_info_->reflection.get();
return metadata;
}
// ===================================================================
struct DynamicMessageFactory::PrototypeMap {
typedef hash_map<const Descriptor*, const DynamicMessage::TypeInfo*> Map;
Map map_;
};
DynamicMessageFactory::DynamicMessageFactory()
: pool_(NULL), delegate_to_generated_factory_(false),
prototypes_(new PrototypeMap) {
}
DynamicMessageFactory::DynamicMessageFactory(const DescriptorPool* pool)
: pool_(pool), delegate_to_generated_factory_(false),
prototypes_(new PrototypeMap) {
}
DynamicMessageFactory::~DynamicMessageFactory() {
for (PrototypeMap::Map::iterator iter = prototypes_->map_.begin();
iter != prototypes_->map_.end(); ++iter) {
delete iter->second;
}
}
const Message* DynamicMessageFactory::GetPrototype(const Descriptor* type) {
MutexLock lock(&prototypes_mutex_);
return GetPrototypeNoLock(type);
}
const Message* DynamicMessageFactory::GetPrototypeNoLock(
const Descriptor* type) {
if (delegate_to_generated_factory_ &&
type->file()->pool() == DescriptorPool::generated_pool()) {
return MessageFactory::generated_factory()->GetPrototype(type);
}
const DynamicMessage::TypeInfo** target = &prototypes_->map_[type];
if (*target != NULL) {
// Already exists.
return (*target)->prototype.get();
}
DynamicMessage::TypeInfo* type_info = new DynamicMessage::TypeInfo;
*target = type_info;
type_info->type = type;
type_info->pool = (pool_ == NULL) ? type->file()->pool() : pool_;
type_info->factory = this;
// We need to construct all the structures passed to
// GeneratedMessageReflection's constructor. This includes:
// - A block of memory that contains space for all the message's fields.
// - An array of integers indicating the byte offset of each field within
// this block.
// - A big bitfield containing a bit for each field indicating whether
// or not that field is set.
// Compute size and offsets.
int* offsets = new int[type->field_count()];
type_info->offsets.reset(offsets);
// Decide all field offsets by packing in order.
// We place the DynamicMessage object itself at the beginning of the allocated
// space.
int size = sizeof(DynamicMessage);
size = AlignOffset(size);
// Next the has_bits, which is an array of uint32s.
type_info->has_bits_offset = size;
int has_bits_array_size =
DivideRoundingUp(type->field_count(), bitsizeof(uint32));
size += has_bits_array_size * sizeof(uint32);
size = AlignOffset(size);
// The ExtensionSet, if any.
if (type->extension_range_count() > 0) {
type_info->extensions_offset = size;
size += sizeof(ExtensionSet);
size = AlignOffset(size);
} else {
// No extensions.
type_info->extensions_offset = -1;
}
// All the fields.
for (int i = 0; i < type->field_count(); i++) {
// Make sure field is aligned to avoid bus errors.
int field_size = FieldSpaceUsed(type->field(i));
size = AlignTo(size, min(kSafeAlignment, field_size));
offsets[i] = size;
size += field_size;
}
// Add the UnknownFieldSet to the end.
size = AlignOffset(size);
type_info->unknown_fields_offset = size;
size += sizeof(UnknownFieldSet);
// Align the final size to make sure no clever allocators think that
// alignment is not necessary.
size = AlignOffset(size);
type_info->size = size;
// Allocate the prototype.
void* base = operator new(size);
memset(base, 0, size);
DynamicMessage* prototype = new(base) DynamicMessage(type_info);
type_info->prototype.reset(prototype);
// Construct the reflection object.
type_info->reflection.reset(
new GeneratedMessageReflection(
type_info->type,
type_info->prototype.get(),
type_info->offsets.get(),
type_info->has_bits_offset,
type_info->unknown_fields_offset,
type_info->extensions_offset,
type_info->pool,
this,
type_info->size));
// Cross link prototypes.
prototype->CrossLinkPrototypes();
return prototype;
}
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,136 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Defines an implementation of Message which can emulate types which are not
// known at compile-time.
#ifndef GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__
#define GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__
#include <google/protobuf/message.h>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
// Defined in other files.
class Descriptor; // descriptor.h
class DescriptorPool; // descriptor.h
// Constructs implementations of Message which can emulate types which are not
// known at compile-time.
//
// Sometimes you want to be able to manipulate protocol types that you don't
// know about at compile time. It would be nice to be able to construct
// a Message object which implements the message type given by any arbitrary
// Descriptor. DynamicMessage provides this.
//
// As it turns out, a DynamicMessage needs to construct extra
// information about its type in order to operate. Most of this information
// can be shared between all DynamicMessages of the same type. But, caching
// this information in some sort of global map would be a bad idea, since
// the cached information for a particular descriptor could outlive the
// descriptor itself. To avoid this problem, DynamicMessageFactory
// encapsulates this "cache". All DynamicMessages of the same type created
// from the same factory will share the same support data. Any Descriptors
// used with a particular factory must outlive the factory.
class LIBPROTOBUF_EXPORT DynamicMessageFactory : public MessageFactory {
public:
// Construct a DynamicMessageFactory that will search for extensions in
// the DescriptorPool in which the exendee is defined.
DynamicMessageFactory();
// Construct a DynamicMessageFactory that will search for extensions in
// the given DescriptorPool.
//
// DEPRECATED: Use CodedInputStream::SetExtensionRegistry() to tell the
// parser to look for extensions in an alternate pool. However, note that
// this is almost never what you want to do. Almost all users should use
// the zero-arg constructor.
DynamicMessageFactory(const DescriptorPool* pool);
~DynamicMessageFactory();
// Call this to tell the DynamicMessageFactory that if it is given a
// Descriptor d for which:
// d->file()->pool() == DescriptorPool::generated_pool(),
// then it should delegate to MessageFactory::generated_factory() instead
// of constructing a dynamic implementation of the message. In theory there
// is no down side to doing this, so it may become the default in the future.
void SetDelegateToGeneratedFactory(bool enable) {
delegate_to_generated_factory_ = enable;
}
// implements MessageFactory ---------------------------------------
// Given a Descriptor, constructs the default (prototype) Message of that
// type. You can then call that message's New() method to construct a
// mutable message of that type.
//
// Calling this method twice with the same Descriptor returns the same
// object. The returned object remains property of the factory and will
// be destroyed when the factory is destroyed. Also, any objects created
// by calling the prototype's New() method share some data with the
// prototype, so these must be destoyed before the DynamicMessageFactory
// is destroyed.
//
// The given descriptor must outlive the returned message, and hence must
// outlive the DynamicMessageFactory.
//
// The method is thread-safe.
const Message* GetPrototype(const Descriptor* type);
private:
const DescriptorPool* pool_;
bool delegate_to_generated_factory_;
// This struct just contains a hash_map. We can't #include <google/protobuf/stubs/hash.h> from
// this header due to hacks needed for hash_map portability in the open source
// release. Namely, stubs/hash.h, which defines hash_map portably, is not a
// public header (for good reason), but dynamic_message.h is, and public
// headers may only #include other public headers.
struct PrototypeMap;
scoped_ptr<PrototypeMap> prototypes_;
mutable Mutex prototypes_mutex_;
friend class DynamicMessage;
const Message* GetPrototypeNoLock(const Descriptor* type);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMessageFactory);
};
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__

View file

@ -0,0 +1,162 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Since the reflection interface for DynamicMessage is implemented by
// GenericMessageReflection, the only thing we really have to test is
// that DynamicMessage correctly sets up the information that
// GenericMessageReflection needs to use. So, we focus on that in this
// test. Other tests, such as generic_message_reflection_unittest and
// reflection_ops_unittest, cover the rest of the functionality used by
// DynamicMessage.
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
class DynamicMessageTest : public testing::Test {
protected:
DescriptorPool pool_;
DynamicMessageFactory factory_;
const Descriptor* descriptor_;
const Message* prototype_;
const Descriptor* extensions_descriptor_;
const Message* extensions_prototype_;
const Descriptor* packed_descriptor_;
const Message* packed_prototype_;
DynamicMessageTest(): factory_(&pool_) {}
virtual void SetUp() {
// We want to make sure that DynamicMessage works (particularly with
// extensions) even if we use descriptors that are *not* from compiled-in
// types, so we make copies of the descriptors for unittest.proto and
// unittest_import.proto.
FileDescriptorProto unittest_file;
FileDescriptorProto unittest_import_file;
unittest::TestAllTypes::descriptor()->file()->CopyTo(&unittest_file);
unittest_import::ImportMessage::descriptor()->file()->CopyTo(
&unittest_import_file);
ASSERT_TRUE(pool_.BuildFile(unittest_import_file) != NULL);
ASSERT_TRUE(pool_.BuildFile(unittest_file) != NULL);
descriptor_ = pool_.FindMessageTypeByName("protobuf_unittest.TestAllTypes");
ASSERT_TRUE(descriptor_ != NULL);
prototype_ = factory_.GetPrototype(descriptor_);
extensions_descriptor_ =
pool_.FindMessageTypeByName("protobuf_unittest.TestAllExtensions");
ASSERT_TRUE(extensions_descriptor_ != NULL);
extensions_prototype_ = factory_.GetPrototype(extensions_descriptor_);
packed_descriptor_ =
pool_.FindMessageTypeByName("protobuf_unittest.TestPackedTypes");
ASSERT_TRUE(packed_descriptor_ != NULL);
packed_prototype_ = factory_.GetPrototype(packed_descriptor_);
}
};
TEST_F(DynamicMessageTest, Descriptor) {
// Check that the descriptor on the DynamicMessage matches the descriptor
// passed to GetPrototype().
EXPECT_EQ(prototype_->GetDescriptor(), descriptor_);
}
TEST_F(DynamicMessageTest, OnePrototype) {
// Check that requesting the same prototype twice produces the same object.
EXPECT_EQ(prototype_, factory_.GetPrototype(descriptor_));
}
TEST_F(DynamicMessageTest, Defaults) {
// Check that all default values are set correctly in the initial message.
TestUtil::ReflectionTester reflection_tester(descriptor_);
reflection_tester.ExpectClearViaReflection(*prototype_);
}
TEST_F(DynamicMessageTest, IndependentOffsets) {
// Check that all fields have independent offsets by setting each
// one to a unique value then checking that they all still have those
// unique values (i.e. they don't stomp each other).
scoped_ptr<Message> message(prototype_->New());
TestUtil::ReflectionTester reflection_tester(descriptor_);
reflection_tester.SetAllFieldsViaReflection(message.get());
reflection_tester.ExpectAllFieldsSetViaReflection(*message);
}
TEST_F(DynamicMessageTest, Extensions) {
// Check that extensions work.
scoped_ptr<Message> message(extensions_prototype_->New());
TestUtil::ReflectionTester reflection_tester(extensions_descriptor_);
reflection_tester.SetAllFieldsViaReflection(message.get());
reflection_tester.ExpectAllFieldsSetViaReflection(*message);
}
TEST_F(DynamicMessageTest, PackedFields) {
// Check that packed fields work properly.
scoped_ptr<Message> message(packed_prototype_->New());
TestUtil::ReflectionTester reflection_tester(packed_descriptor_);
reflection_tester.SetPackedFieldsViaReflection(message.get());
reflection_tester.ExpectPackedFieldsSetViaReflection(*message);
}
TEST_F(DynamicMessageTest, SpaceUsed) {
// Test that SpaceUsed() works properly
// Since we share the implementation with generated messages, we don't need
// to test very much here. Just make sure it appears to be working.
scoped_ptr<Message> message(prototype_->New());
TestUtil::ReflectionTester reflection_tester(descriptor_);
int initial_space_used = message->SpaceUsed();
reflection_tester.SetAllFieldsViaReflection(message.get());
EXPECT_LT(initial_space_used, message->SpaceUsed());
}
} // namespace protobuf
} // namespace google

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,902 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This header is logically internal, but is made public because it is used
// from protocol-compiler-generated code, which may reside in other components.
#ifndef GOOGLE_PROTOBUF_EXTENSION_SET_H__
#define GOOGLE_PROTOBUF_EXTENSION_SET_H__
#include <vector>
#include <stack>
#include <map>
#include <utility>
#include <string>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
class Descriptor; // descriptor.h
class FieldDescriptor; // descriptor.h
class DescriptorPool; // descriptor.h
class MessageLite; // message_lite.h
class Message; // message.h
class MessageFactory; // message.h
class UnknownFieldSet; // unknown_field_set.h
namespace io {
class CodedInputStream; // coded_stream.h
class CodedOutputStream; // coded_stream.h
}
namespace internal {
class FieldSkipper; // wire_format_lite.h
class RepeatedPtrFieldBase; // repeated_field.h
}
template <typename Element> class RepeatedField; // repeated_field.h
template <typename Element> class RepeatedPtrField; // repeated_field.h
}
namespace protobuf {
namespace internal {
// Used to store values of type WireFormatLite::FieldType without having to
// #include wire_format_lite.h. Also, ensures that we use only one byte to
// store these values, which is important to keep the layout of
// ExtensionSet::Extension small.
typedef uint8 FieldType;
// A function which, given an integer value, returns true if the number
// matches one of the defined values for the corresponding enum type. This
// is used with RegisterEnumExtension, below.
typedef bool EnumValidityFunc(int number);
// Version of the above which takes an argument. This is needed to deal with
// extensions that are not compiled in.
typedef bool EnumValidityFuncWithArg(const void* arg, int number);
// Information about a registered extension.
struct ExtensionInfo {
inline ExtensionInfo() {}
inline ExtensionInfo(FieldType type, bool is_repeated, bool is_packed)
: type(type), is_repeated(is_repeated), is_packed(is_packed),
descriptor(NULL) {}
FieldType type;
bool is_repeated;
bool is_packed;
struct EnumValidityCheck {
EnumValidityFuncWithArg* func;
const void* arg;
};
union {
EnumValidityCheck enum_validity_check;
const MessageLite* message_prototype;
};
// The descriptor for this extension, if one exists and is known. May be
// NULL. Must not be NULL if the descriptor for the extension does not
// live in the same pool as the descriptor for the containing type.
const FieldDescriptor* descriptor;
};
// Abstract interface for an object which looks up extension definitions. Used
// when parsing.
class LIBPROTOBUF_EXPORT ExtensionFinder {
public:
virtual ~ExtensionFinder();
// Find the extension with the given containing type and number.
virtual bool Find(int number, ExtensionInfo* output) = 0;
};
// Implementation of ExtensionFinder which finds extensions defined in .proto
// files which have been compiled into the binary.
class LIBPROTOBUF_EXPORT GeneratedExtensionFinder : public ExtensionFinder {
public:
GeneratedExtensionFinder(const MessageLite* containing_type)
: containing_type_(containing_type) {}
virtual ~GeneratedExtensionFinder() {}
// Returns true and fills in *output if found, otherwise returns false.
virtual bool Find(int number, ExtensionInfo* output);
private:
const MessageLite* containing_type_;
};
// Note: extension_set_heavy.cc defines DescriptorPoolExtensionFinder for
// finding extensions from a DescriptorPool.
// This is an internal helper class intended for use within the protocol buffer
// library and generated classes. Clients should not use it directly. Instead,
// use the generated accessors such as GetExtension() of the class being
// extended.
//
// This class manages extensions for a protocol message object. The
// message's HasExtension(), GetExtension(), MutableExtension(), and
// ClearExtension() methods are just thin wrappers around the embedded
// ExtensionSet. When parsing, if a tag number is encountered which is
// inside one of the message type's extension ranges, the tag is passed
// off to the ExtensionSet for parsing. Etc.
class LIBPROTOBUF_EXPORT ExtensionSet {
public:
ExtensionSet();
~ExtensionSet();
// These are called at startup by protocol-compiler-generated code to
// register known extensions. The registrations are used by ParseField()
// to look up extensions for parsed field numbers. Note that dynamic parsing
// does not use ParseField(); only protocol-compiler-generated parsing
// methods do.
static void RegisterExtension(const MessageLite* containing_type,
int number, FieldType type,
bool is_repeated, bool is_packed);
static void RegisterEnumExtension(const MessageLite* containing_type,
int number, FieldType type,
bool is_repeated, bool is_packed,
EnumValidityFunc* is_valid);
static void RegisterMessageExtension(const MessageLite* containing_type,
int number, FieldType type,
bool is_repeated, bool is_packed,
const MessageLite* prototype);
// =================================================================
// Add all fields which are currently present to the given vector. This
// is useful to implement Reflection::ListFields().
void AppendToList(const Descriptor* containing_type,
const DescriptorPool* pool,
vector<const FieldDescriptor*>* output) const;
// =================================================================
// Accessors
//
// Generated message classes include type-safe templated wrappers around
// these methods. Generally you should use those rather than call these
// directly, unless you are doing low-level memory management.
//
// When calling any of these accessors, the extension number requested
// MUST exist in the DescriptorPool provided to the constructor. Otheriwse,
// the method will fail an assert. Normally, though, you would not call
// these directly; you would either call the generated accessors of your
// message class (e.g. GetExtension()) or you would call the accessors
// of the reflection interface. In both cases, it is impossible to
// trigger this assert failure: the generated accessors only accept
// linked-in extension types as parameters, while the Reflection interface
// requires you to provide the FieldDescriptor describing the extension.
//
// When calling any of these accessors, a protocol-compiler-generated
// implementation of the extension corresponding to the number MUST
// be linked in, and the FieldDescriptor used to refer to it MUST be
// the one generated by that linked-in code. Otherwise, the method will
// die on an assert failure. The message objects returned by the message
// accessors are guaranteed to be of the correct linked-in type.
//
// These methods pretty much match Reflection except that:
// - They're not virtual.
// - They identify fields by number rather than FieldDescriptors.
// - They identify enum values using integers rather than descriptors.
// - Strings provide Mutable() in addition to Set() accessors.
bool Has(int number) const;
int ExtensionSize(int number) const; // Size of a repeated extension.
void ClearExtension(int number);
// singular fields -------------------------------------------------
int32 GetInt32 (int number, int32 default_value) const;
int64 GetInt64 (int number, int64 default_value) const;
uint32 GetUInt32(int number, uint32 default_value) const;
uint64 GetUInt64(int number, uint64 default_value) const;
float GetFloat (int number, float default_value) const;
double GetDouble(int number, double default_value) const;
bool GetBool (int number, bool default_value) const;
int GetEnum (int number, int default_value) const;
const string & GetString (int number, const string& default_value) const;
const MessageLite& GetMessage(int number,
const MessageLite& default_value) const;
const MessageLite& GetMessage(int number, const Descriptor* message_type,
MessageFactory* factory) const;
// |descriptor| may be NULL so long as it is known that the descriptor for
// the extension lives in the same pool as the descriptor for the containing
// type.
#define desc const FieldDescriptor* descriptor // avoid line wrapping
void SetInt32 (int number, FieldType type, int32 value, desc);
void SetInt64 (int number, FieldType type, int64 value, desc);
void SetUInt32(int number, FieldType type, uint32 value, desc);
void SetUInt64(int number, FieldType type, uint64 value, desc);
void SetFloat (int number, FieldType type, float value, desc);
void SetDouble(int number, FieldType type, double value, desc);
void SetBool (int number, FieldType type, bool value, desc);
void SetEnum (int number, FieldType type, int value, desc);
void SetString(int number, FieldType type, const string& value, desc);
string * MutableString (int number, FieldType type, desc);
MessageLite* MutableMessage(int number, FieldType type,
const MessageLite& prototype, desc);
MessageLite* MutableMessage(const FieldDescriptor* decsriptor,
MessageFactory* factory);
#undef desc
// repeated fields -------------------------------------------------
int32 GetRepeatedInt32 (int number, int index) const;
int64 GetRepeatedInt64 (int number, int index) const;
uint32 GetRepeatedUInt32(int number, int index) const;
uint64 GetRepeatedUInt64(int number, int index) const;
float GetRepeatedFloat (int number, int index) const;
double GetRepeatedDouble(int number, int index) const;
bool GetRepeatedBool (int number, int index) const;
int GetRepeatedEnum (int number, int index) const;
const string & GetRepeatedString (int number, int index) const;
const MessageLite& GetRepeatedMessage(int number, int index) const;
void SetRepeatedInt32 (int number, int index, int32 value);
void SetRepeatedInt64 (int number, int index, int64 value);
void SetRepeatedUInt32(int number, int index, uint32 value);
void SetRepeatedUInt64(int number, int index, uint64 value);
void SetRepeatedFloat (int number, int index, float value);
void SetRepeatedDouble(int number, int index, double value);
void SetRepeatedBool (int number, int index, bool value);
void SetRepeatedEnum (int number, int index, int value);
void SetRepeatedString(int number, int index, const string& value);
string * MutableRepeatedString (int number, int index);
MessageLite* MutableRepeatedMessage(int number, int index);
#define desc const FieldDescriptor* descriptor // avoid line wrapping
void AddInt32 (int number, FieldType type, bool packed, int32 value, desc);
void AddInt64 (int number, FieldType type, bool packed, int64 value, desc);
void AddUInt32(int number, FieldType type, bool packed, uint32 value, desc);
void AddUInt64(int number, FieldType type, bool packed, uint64 value, desc);
void AddFloat (int number, FieldType type, bool packed, float value, desc);
void AddDouble(int number, FieldType type, bool packed, double value, desc);
void AddBool (int number, FieldType type, bool packed, bool value, desc);
void AddEnum (int number, FieldType type, bool packed, int value, desc);
void AddString(int number, FieldType type, const string& value, desc);
string * AddString (int number, FieldType type, desc);
MessageLite* AddMessage(int number, FieldType type,
const MessageLite& prototype, desc);
MessageLite* AddMessage(const FieldDescriptor* descriptor,
MessageFactory* factory);
#undef desc
void RemoveLast(int number);
void SwapElements(int number, int index1, int index2);
// -----------------------------------------------------------------
// TODO(kenton): Hardcore memory management accessors
// =================================================================
// convenience methods for implementing methods of Message
//
// These could all be implemented in terms of the other methods of this
// class, but providing them here helps keep the generated code size down.
void Clear();
void MergeFrom(const ExtensionSet& other);
void Swap(ExtensionSet* other);
bool IsInitialized() const;
// Parses a single extension from the input. The input should start out
// positioned immediately after the tag. |containing_type| is the default
// instance for the containing message; it is used only to look up the
// extension by number. See RegisterExtension(), above. Unlike the other
// methods of ExtensionSet, this only works for generated message types --
// it looks up extensions registered using RegisterExtension().
bool ParseField(uint32 tag, io::CodedInputStream* input,
ExtensionFinder* extension_finder,
FieldSkipper* field_skipper);
// Specific versions for lite or full messages (constructs the appropriate
// FieldSkipper automatically).
bool ParseField(uint32 tag, io::CodedInputStream* input,
const MessageLite* containing_type);
bool ParseField(uint32 tag, io::CodedInputStream* input,
const Message* containing_type,
UnknownFieldSet* unknown_fields);
// Parse an entire message in MessageSet format. Such messages have no
// fields, only extensions.
bool ParseMessageSet(io::CodedInputStream* input,
ExtensionFinder* extension_finder,
FieldSkipper* field_skipper);
// Specific versions for lite or full messages (constructs the appropriate
// FieldSkipper automatically).
bool ParseMessageSet(io::CodedInputStream* input,
const MessageLite* containing_type);
bool ParseMessageSet(io::CodedInputStream* input,
const Message* containing_type,
UnknownFieldSet* unknown_fields);
// Write all extension fields with field numbers in the range
// [start_field_number, end_field_number)
// to the output stream, using the cached sizes computed when ByteSize() was
// last called. Note that the range bounds are inclusive-exclusive.
void SerializeWithCachedSizes(int start_field_number,
int end_field_number,
io::CodedOutputStream* output) const;
// Same as SerializeWithCachedSizes, but without any bounds checking.
// The caller must ensure that target has sufficient capacity for the
// serialized extensions.
//
// Returns a pointer past the last written byte.
uint8* SerializeWithCachedSizesToArray(int start_field_number,
int end_field_number,
uint8* target) const;
// Like above but serializes in MessageSet format.
void SerializeMessageSetWithCachedSizes(io::CodedOutputStream* output) const;
uint8* SerializeMessageSetWithCachedSizesToArray(uint8* target) const;
// Returns the total serialized size of all the extensions.
int ByteSize() const;
// Like ByteSize() but uses MessageSet format.
int MessageSetByteSize() const;
// Returns (an estimate of) the total number of bytes used for storing the
// extensions in memory, excluding sizeof(*this). If the ExtensionSet is
// for a lite message (and thus possibly contains lite messages), the results
// are undefined (might work, might crash, might corrupt data, might not even
// be linked in). It's up to the protocol compiler to avoid calling this on
// such ExtensionSets (easy enough since lite messages don't implement
// SpaceUsed()).
int SpaceUsedExcludingSelf() const;
private:
struct Extension {
union {
int32 int32_value;
int64 int64_value;
uint32 uint32_value;
uint64 uint64_value;
float float_value;
double double_value;
bool bool_value;
int enum_value;
string* string_value;
MessageLite* message_value;
RepeatedField <int32 >* repeated_int32_value;
RepeatedField <int64 >* repeated_int64_value;
RepeatedField <uint32 >* repeated_uint32_value;
RepeatedField <uint64 >* repeated_uint64_value;
RepeatedField <float >* repeated_float_value;
RepeatedField <double >* repeated_double_value;
RepeatedField <bool >* repeated_bool_value;
RepeatedField <int >* repeated_enum_value;
RepeatedPtrField<string >* repeated_string_value;
RepeatedPtrField<MessageLite>* repeated_message_value;
};
FieldType type;
bool is_repeated;
// For singular types, indicates if the extension is "cleared". This
// happens when an extension is set and then later cleared by the caller.
// We want to keep the Extension object around for reuse, so instead of
// removing it from the map, we just set is_cleared = true. This has no
// meaning for repeated types; for those, the size of the RepeatedField
// simply becomes zero when cleared.
bool is_cleared;
// For repeated types, this indicates if the [packed=true] option is set.
bool is_packed;
// The descriptor for this extension, if one exists and is known. May be
// NULL. Must not be NULL if the descriptor for the extension does not
// live in the same pool as the descriptor for the containing type.
const FieldDescriptor* descriptor;
// For packed fields, the size of the packed data is recorded here when
// ByteSize() is called then used during serialization.
// TODO(kenton): Use atomic<int> when C++ supports it.
mutable int cached_size;
// Some helper methods for operations on a single Extension.
void SerializeFieldWithCachedSizes(
int number,
io::CodedOutputStream* output) const;
uint8* SerializeFieldWithCachedSizesToArray(
int number,
uint8* target) const;
void SerializeMessageSetItemWithCachedSizes(
int number,
io::CodedOutputStream* output) const;
uint8* SerializeMessageSetItemWithCachedSizesToArray(
int number,
uint8* target) const;
int ByteSize(int number) const;
int MessageSetItemByteSize(int number) const;
void Clear();
int GetSize() const;
void Free();
int SpaceUsedExcludingSelf() const;
};
// Gets the extension with the given number, creating it if it does not
// already exist. Returns true if the extension did not already exist.
bool MaybeNewExtension(int number, const FieldDescriptor* descriptor,
Extension** result);
// Parse a single MessageSet item -- called just after the item group start
// tag has been read.
bool ParseMessageSetItem(io::CodedInputStream* input,
ExtensionFinder* extension_finder,
FieldSkipper* field_skipper);
// Hack: RepeatedPtrFieldBase declares ExtensionSet as a friend. This
// friendship should automatically extend to ExtensionSet::Extension, but
// unfortunately some older compilers (e.g. GCC 3.4.4) do not implement this
// correctly. So, we must provide helpers for calling methods of that
// class.
// Defined in extension_set_heavy.cc.
static inline int RepeatedMessage_SpaceUsedExcludingSelf(
RepeatedPtrFieldBase* field);
// The Extension struct is small enough to be passed by value, so we use it
// directly as the value type in the map rather than use pointers. We use
// a map rather than hash_map here because we expect most ExtensionSets will
// only contain a small number of extensions whereas hash_map is optimized
// for 100 elements or more. Also, we want AppendToList() to order fields
// by field number.
map<int, Extension> extensions_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionSet);
};
// These are just for convenience...
inline void ExtensionSet::SetString(int number, FieldType type,
const string& value,
const FieldDescriptor* descriptor) {
MutableString(number, type, descriptor)->assign(value);
}
inline void ExtensionSet::SetRepeatedString(int number, int index,
const string& value) {
MutableRepeatedString(number, index)->assign(value);
}
inline void ExtensionSet::AddString(int number, FieldType type,
const string& value,
const FieldDescriptor* descriptor) {
AddString(number, type, descriptor)->assign(value);
}
// ===================================================================
// Glue for generated extension accessors
// -------------------------------------------------------------------
// Template magic
// First we have a set of classes representing "type traits" for different
// field types. A type traits class knows how to implement basic accessors
// for extensions of a particular type given an ExtensionSet. The signature
// for a type traits class looks like this:
//
// class TypeTraits {
// public:
// typedef ? ConstType;
// typedef ? MutableType;
//
// static inline ConstType Get(int number, const ExtensionSet& set);
// static inline void Set(int number, ConstType value, ExtensionSet* set);
// static inline MutableType Mutable(int number, ExtensionSet* set);
//
// // Variants for repeated fields.
// static inline ConstType Get(int number, const ExtensionSet& set,
// int index);
// static inline void Set(int number, int index,
// ConstType value, ExtensionSet* set);
// static inline MutableType Mutable(int number, int index,
// ExtensionSet* set);
// static inline void Add(int number, ConstType value, ExtensionSet* set);
// static inline MutableType Add(int number, ExtensionSet* set);
// };
//
// Not all of these methods make sense for all field types. For example, the
// "Mutable" methods only make sense for strings and messages, and the
// repeated methods only make sense for repeated types. So, each type
// traits class implements only the set of methods from this signature that it
// actually supports. This will cause a compiler error if the user tries to
// access an extension using a method that doesn't make sense for its type.
// For example, if "foo" is an extension of type "optional int32", then if you
// try to write code like:
// my_message.MutableExtension(foo)
// you will get a compile error because PrimitiveTypeTraits<int32> does not
// have a "Mutable()" method.
// -------------------------------------------------------------------
// PrimitiveTypeTraits
// Since the ExtensionSet has different methods for each primitive type,
// we must explicitly define the methods of the type traits class for each
// known type.
template <typename Type>
class PrimitiveTypeTraits {
public:
typedef Type ConstType;
static inline ConstType Get(int number, const ExtensionSet& set,
ConstType default_value);
static inline void Set(int number, FieldType field_type,
ConstType value, ExtensionSet* set);
};
template <typename Type>
class RepeatedPrimitiveTypeTraits {
public:
typedef Type ConstType;
static inline Type Get(int number, const ExtensionSet& set, int index);
static inline void Set(int number, int index, Type value, ExtensionSet* set);
static inline void Add(int number, FieldType field_type,
bool is_packed, Type value, ExtensionSet* set);
};
#define PROTOBUF_DEFINE_PRIMITIVE_TYPE(TYPE, METHOD) \
template<> inline TYPE PrimitiveTypeTraits<TYPE>::Get( \
int number, const ExtensionSet& set, TYPE default_value) { \
return set.Get##METHOD(number, default_value); \
} \
template<> inline void PrimitiveTypeTraits<TYPE>::Set( \
int number, FieldType field_type, TYPE value, ExtensionSet* set) { \
set->Set##METHOD(number, field_type, value, NULL); \
} \
\
template<> inline TYPE RepeatedPrimitiveTypeTraits<TYPE>::Get( \
int number, const ExtensionSet& set, int index) { \
return set.GetRepeated##METHOD(number, index); \
} \
template<> inline void RepeatedPrimitiveTypeTraits<TYPE>::Set( \
int number, int index, TYPE value, ExtensionSet* set) { \
set->SetRepeated##METHOD(number, index, value); \
} \
template<> inline void RepeatedPrimitiveTypeTraits<TYPE>::Add( \
int number, FieldType field_type, bool is_packed, \
TYPE value, ExtensionSet* set) { \
set->Add##METHOD(number, field_type, is_packed, value, NULL); \
}
PROTOBUF_DEFINE_PRIMITIVE_TYPE( int32, Int32)
PROTOBUF_DEFINE_PRIMITIVE_TYPE( int64, Int64)
PROTOBUF_DEFINE_PRIMITIVE_TYPE(uint32, UInt32)
PROTOBUF_DEFINE_PRIMITIVE_TYPE(uint64, UInt64)
PROTOBUF_DEFINE_PRIMITIVE_TYPE( float, Float)
PROTOBUF_DEFINE_PRIMITIVE_TYPE(double, Double)
PROTOBUF_DEFINE_PRIMITIVE_TYPE( bool, Bool)
#undef PROTOBUF_DEFINE_PRIMITIVE_TYPE
// -------------------------------------------------------------------
// StringTypeTraits
// Strings support both Set() and Mutable().
class LIBPROTOBUF_EXPORT StringTypeTraits {
public:
typedef const string& ConstType;
typedef string* MutableType;
static inline const string& Get(int number, const ExtensionSet& set,
ConstType default_value) {
return set.GetString(number, default_value);
}
static inline void Set(int number, FieldType field_type,
const string& value, ExtensionSet* set) {
set->SetString(number, field_type, value, NULL);
}
static inline string* Mutable(int number, FieldType field_type,
ExtensionSet* set) {
return set->MutableString(number, field_type, NULL);
}
};
class LIBPROTOBUF_EXPORT RepeatedStringTypeTraits {
public:
typedef const string& ConstType;
typedef string* MutableType;
static inline const string& Get(int number, const ExtensionSet& set,
int index) {
return set.GetRepeatedString(number, index);
}
static inline void Set(int number, int index,
const string& value, ExtensionSet* set) {
set->SetRepeatedString(number, index, value);
}
static inline string* Mutable(int number, int index, ExtensionSet* set) {
return set->MutableRepeatedString(number, index);
}
static inline void Add(int number, FieldType field_type,
bool /*is_packed*/, const string& value,
ExtensionSet* set) {
set->AddString(number, field_type, value, NULL);
}
static inline string* Add(int number, FieldType field_type,
ExtensionSet* set) {
return set->AddString(number, field_type, NULL);
}
};
// -------------------------------------------------------------------
// EnumTypeTraits
// ExtensionSet represents enums using integers internally, so we have to
// static_cast around.
template <typename Type, bool IsValid(int)>
class EnumTypeTraits {
public:
typedef Type ConstType;
static inline ConstType Get(int number, const ExtensionSet& set,
ConstType default_value) {
return static_cast<Type>(set.GetEnum(number, default_value));
}
static inline void Set(int number, FieldType field_type,
ConstType value, ExtensionSet* set) {
GOOGLE_DCHECK(IsValid(value));
set->SetEnum(number, field_type, value, NULL);
}
};
template <typename Type, bool IsValid(int)>
class RepeatedEnumTypeTraits {
public:
typedef Type ConstType;
static inline ConstType Get(int number, const ExtensionSet& set, int index) {
return static_cast<Type>(set.GetRepeatedEnum(number, index));
}
static inline void Set(int number, int index,
ConstType value, ExtensionSet* set) {
GOOGLE_DCHECK(IsValid(value));
set->SetRepeatedEnum(number, index, value);
}
static inline void Add(int number, FieldType field_type,
bool is_packed, ConstType value, ExtensionSet* set) {
GOOGLE_DCHECK(IsValid(value));
set->AddEnum(number, field_type, is_packed, value, NULL);
}
};
// -------------------------------------------------------------------
// MessageTypeTraits
// ExtensionSet guarantees that when manipulating extensions with message
// types, the implementation used will be the compiled-in class representing
// that type. So, we can static_cast down to the exact type we expect.
template <typename Type>
class MessageTypeTraits {
public:
typedef const Type& ConstType;
typedef Type* MutableType;
static inline ConstType Get(int number, const ExtensionSet& set,
ConstType default_value) {
return static_cast<const Type&>(
set.GetMessage(number, default_value));
}
static inline MutableType Mutable(int number, FieldType field_type,
ExtensionSet* set) {
return static_cast<Type*>(
set->MutableMessage(number, field_type, Type::default_instance(), NULL));
}
};
template <typename Type>
class RepeatedMessageTypeTraits {
public:
typedef const Type& ConstType;
typedef Type* MutableType;
static inline ConstType Get(int number, const ExtensionSet& set, int index) {
return static_cast<const Type&>(set.GetRepeatedMessage(number, index));
}
static inline MutableType Mutable(int number, int index, ExtensionSet* set) {
return static_cast<Type*>(set->MutableRepeatedMessage(number, index));
}
static inline MutableType Add(int number, FieldType field_type,
ExtensionSet* set) {
return static_cast<Type*>(
set->AddMessage(number, field_type, Type::default_instance(), NULL));
}
};
// -------------------------------------------------------------------
// ExtensionIdentifier
// This is the type of actual extension objects. E.g. if you have:
// extends Foo with optional int32 bar = 1234;
// then "bar" will be defined in C++ as:
// ExtensionIdentifier<Foo, PrimitiveTypeTraits<int32>, 1, false> bar(1234);
//
// Note that we could, in theory, supply the field number as a template
// parameter, and thus make an instance of ExtensionIdentifier have no
// actual contents. However, if we did that, then using at extension
// identifier would not necessarily cause the compiler to output any sort
// of reference to any simple defined in the extension's .pb.o file. Some
// linkers will actually drop object files that are not explicitly referenced,
// but that would be bad because it would cause this extension to not be
// registered at static initialization, and therefore using it would crash.
template <typename ExtendeeType, typename TypeTraitsType,
FieldType field_type, bool is_packed>
class ExtensionIdentifier {
public:
typedef TypeTraitsType TypeTraits;
typedef ExtendeeType Extendee;
ExtensionIdentifier(int number, typename TypeTraits::ConstType default_value)
: number_(number), default_value_(default_value) {}
inline int number() const { return number_; }
typename TypeTraits::ConstType default_value() const {
return default_value_;
}
private:
const int number_;
typename TypeTraits::ConstType default_value_;
};
// -------------------------------------------------------------------
// Generated accessors
// This macro should be expanded in the context of a generated type which
// has extensions.
//
// We use "_proto_TypeTraits" as a type name below because "TypeTraits"
// causes problems if the class has a nested message or enum type with that
// name and "_TypeTraits" is technically reserved for the C++ library since
// it starts with an underscore followed by a capital letter.
#define GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(CLASSNAME) \
/* Has, Size, Clear */ \
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline bool HasExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id) const { \
return _extensions_.Has(id.number()); \
} \
\
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline void ClearExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id) { \
_extensions_.ClearExtension(id.number()); \
} \
\
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline int ExtensionSize( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id) const { \
return _extensions_.ExtensionSize(id.number()); \
} \
\
/* Singular accessors */ \
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline typename _proto_TypeTraits::ConstType GetExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id) const { \
return _proto_TypeTraits::Get(id.number(), _extensions_, \
id.default_value()); \
} \
\
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline typename _proto_TypeTraits::MutableType MutableExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id) { \
return _proto_TypeTraits::Mutable(id.number(), field_type, &_extensions_);\
} \
\
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline void SetExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id, \
typename _proto_TypeTraits::ConstType value) { \
_proto_TypeTraits::Set(id.number(), field_type, value, &_extensions_); \
} \
\
/* Repeated accessors */ \
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline typename _proto_TypeTraits::ConstType GetExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id, \
int index) const { \
return _proto_TypeTraits::Get(id.number(), _extensions_, index); \
} \
\
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline typename _proto_TypeTraits::MutableType MutableExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id, \
int index) { \
return _proto_TypeTraits::Mutable(id.number(), index, &_extensions_); \
} \
\
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline void SetExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id, \
int index, typename _proto_TypeTraits::ConstType value) { \
_proto_TypeTraits::Set(id.number(), index, value, &_extensions_); \
} \
\
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline typename _proto_TypeTraits::MutableType AddExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id) { \
return _proto_TypeTraits::Add(id.number(), field_type, &_extensions_); \
} \
\
template <typename _proto_TypeTraits, \
::google::protobuf::internal::FieldType field_type, \
bool is_packed> \
inline void AddExtension( \
const ::google::protobuf::internal::ExtensionIdentifier< \
CLASSNAME, _proto_TypeTraits, field_type, is_packed>& id, \
typename _proto_TypeTraits::ConstType value) { \
_proto_TypeTraits::Add(id.number(), field_type, is_packed, \
value, &_extensions_); \
}
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_EXTENSION_SET_H__

View file

@ -0,0 +1,457 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Contains methods defined in extension_set.h which cannot be part of the
// lite library because they use descriptors or reflection.
#include <google/protobuf/extension_set.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/wire_format_lite_inl.h>
namespace google {
namespace protobuf {
namespace internal {
// Implementation of ExtensionFinder which finds extensions in a given
// DescriptorPool, using the given MessageFactory to construct sub-objects.
// This class is implemented in extension_set_heavy.cc.
class DescriptorPoolExtensionFinder : public ExtensionFinder {
public:
DescriptorPoolExtensionFinder(const DescriptorPool* pool,
MessageFactory* factory,
const Descriptor* containing_type)
: pool_(pool), factory_(factory), containing_type_(containing_type) {}
virtual ~DescriptorPoolExtensionFinder() {}
virtual bool Find(int number, ExtensionInfo* output);
private:
const DescriptorPool* pool_;
MessageFactory* factory_;
const Descriptor* containing_type_;
};
void ExtensionSet::AppendToList(const Descriptor* containing_type,
const DescriptorPool* pool,
vector<const FieldDescriptor*>* output) const {
for (map<int, Extension>::const_iterator iter = extensions_.begin();
iter != extensions_.end(); ++iter) {
bool has = false;
if (iter->second.is_repeated) {
has = iter->second.GetSize() > 0;
} else {
has = !iter->second.is_cleared;
}
if (has) {
// TODO(kenton): Looking up each field by number is somewhat unfortunate.
// Is there a better way? The problem is that descriptors are lazily-
// initialized, so they might not even be constructed until
// AppendToList() is called.
if (iter->second.descriptor == NULL) {
output->push_back(pool->FindExtensionByNumber(
containing_type, iter->first));
} else {
output->push_back(iter->second.descriptor);
}
}
}
}
inline FieldDescriptor::Type real_type(FieldType type) {
GOOGLE_DCHECK(type > 0 && type <= FieldDescriptor::MAX_TYPE);
return static_cast<FieldDescriptor::Type>(type);
}
inline FieldDescriptor::CppType cpp_type(FieldType type) {
return FieldDescriptor::TypeToCppType(
static_cast<FieldDescriptor::Type>(type));
}
#define GOOGLE_DCHECK_TYPE(EXTENSION, LABEL, CPPTYPE) \
GOOGLE_DCHECK_EQ((EXTENSION).is_repeated ? FieldDescriptor::LABEL_REPEATED \
: FieldDescriptor::LABEL_OPTIONAL, \
FieldDescriptor::LABEL_##LABEL); \
GOOGLE_DCHECK_EQ(cpp_type((EXTENSION).type), FieldDescriptor::CPPTYPE_##CPPTYPE)
const MessageLite& ExtensionSet::GetMessage(int number,
const Descriptor* message_type,
MessageFactory* factory) const {
map<int, Extension>::const_iterator iter = extensions_.find(number);
if (iter == extensions_.end() || iter->second.is_cleared) {
// Not present. Return the default value.
return *factory->GetPrototype(message_type);
} else {
GOOGLE_DCHECK_TYPE(iter->second, OPTIONAL, MESSAGE);
return *iter->second.message_value;
}
}
MessageLite* ExtensionSet::MutableMessage(const FieldDescriptor* descriptor,
MessageFactory* factory) {
Extension* extension;
if (MaybeNewExtension(descriptor->number(), descriptor, &extension)) {
extension->type = descriptor->type();
GOOGLE_DCHECK_EQ(cpp_type(extension->type), FieldDescriptor::CPPTYPE_MESSAGE);
extension->is_repeated = false;
extension->is_packed = false;
const MessageLite* prototype =
factory->GetPrototype(descriptor->message_type());
GOOGLE_CHECK(prototype != NULL);
extension->message_value = prototype->New();
} else {
GOOGLE_DCHECK_TYPE(*extension, OPTIONAL, MESSAGE);
}
extension->is_cleared = false;
return extension->message_value;
}
MessageLite* ExtensionSet::AddMessage(const FieldDescriptor* descriptor,
MessageFactory* factory) {
Extension* extension;
if (MaybeNewExtension(descriptor->number(), descriptor, &extension)) {
extension->type = descriptor->type();
GOOGLE_DCHECK_EQ(cpp_type(extension->type), FieldDescriptor::CPPTYPE_MESSAGE);
extension->is_repeated = true;
extension->repeated_message_value =
new RepeatedPtrField<MessageLite>();
} else {
GOOGLE_DCHECK_TYPE(*extension, REPEATED, MESSAGE);
}
// RepeatedPtrField<Message> does not know how to Add() since it cannot
// allocate an abstract object, so we have to be tricky.
MessageLite* result = extension->repeated_message_value
->AddFromCleared<internal::GenericTypeHandler<MessageLite> >();
if (result == NULL) {
const MessageLite* prototype;
if (extension->repeated_message_value->size() == 0) {
prototype = factory->GetPrototype(descriptor->message_type());
GOOGLE_CHECK(prototype != NULL);
} else {
prototype = &extension->repeated_message_value->Get(0);
}
result = prototype->New();
extension->repeated_message_value->AddAllocated(result);
}
return result;
}
static bool ValidateEnumUsingDescriptor(const void* arg, int number) {
return reinterpret_cast<const EnumDescriptor*>(arg)
->FindValueByNumber(number) != NULL;
}
bool DescriptorPoolExtensionFinder::Find(int number, ExtensionInfo* output) {
const FieldDescriptor* extension =
pool_->FindExtensionByNumber(containing_type_, number);
if (extension == NULL) {
return false;
} else {
output->type = extension->type();
output->is_repeated = extension->is_repeated();
output->is_packed = extension->options().packed();
output->descriptor = extension;
if (extension->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
output->message_prototype =
factory_->GetPrototype(extension->message_type());
GOOGLE_CHECK(output->message_prototype != NULL)
<< "Extension factory's GetPrototype() returned NULL for extension: "
<< extension->full_name();
} else if (extension->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
output->enum_validity_check.func = ValidateEnumUsingDescriptor;
output->enum_validity_check.arg = extension->enum_type();
}
return true;
}
}
bool ExtensionSet::ParseField(uint32 tag, io::CodedInputStream* input,
const Message* containing_type,
UnknownFieldSet* unknown_fields) {
UnknownFieldSetFieldSkipper skipper(unknown_fields);
if (input->GetExtensionPool() == NULL) {
GeneratedExtensionFinder finder(containing_type);
return ParseField(tag, input, &finder, &skipper);
} else {
DescriptorPoolExtensionFinder finder(input->GetExtensionPool(),
input->GetExtensionFactory(),
containing_type->GetDescriptor());
return ParseField(tag, input, &finder, &skipper);
}
}
bool ExtensionSet::ParseMessageSet(io::CodedInputStream* input,
const Message* containing_type,
UnknownFieldSet* unknown_fields) {
UnknownFieldSetFieldSkipper skipper(unknown_fields);
if (input->GetExtensionPool() == NULL) {
GeneratedExtensionFinder finder(containing_type);
return ParseMessageSet(input, &finder, &skipper);
} else {
DescriptorPoolExtensionFinder finder(input->GetExtensionPool(),
input->GetExtensionFactory(),
containing_type->GetDescriptor());
return ParseMessageSet(input, &finder, &skipper);
}
}
int ExtensionSet::SpaceUsedExcludingSelf() const {
int total_size =
extensions_.size() * sizeof(map<int, Extension>::value_type);
for (map<int, Extension>::const_iterator iter = extensions_.begin(),
end = extensions_.end();
iter != end;
++iter) {
total_size += iter->second.SpaceUsedExcludingSelf();
}
return total_size;
}
inline int ExtensionSet::RepeatedMessage_SpaceUsedExcludingSelf(
RepeatedPtrFieldBase* field) {
return field->SpaceUsedExcludingSelf<GenericTypeHandler<Message> >();
}
int ExtensionSet::Extension::SpaceUsedExcludingSelf() const {
int total_size = 0;
if (is_repeated) {
switch (cpp_type(type)) {
#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \
case FieldDescriptor::CPPTYPE_##UPPERCASE: \
total_size += sizeof(*repeated_##LOWERCASE##_value) + \
repeated_##LOWERCASE##_value->SpaceUsedExcludingSelf();\
break
HANDLE_TYPE( INT32, int32);
HANDLE_TYPE( INT64, int64);
HANDLE_TYPE( UINT32, uint32);
HANDLE_TYPE( UINT64, uint64);
HANDLE_TYPE( FLOAT, float);
HANDLE_TYPE( DOUBLE, double);
HANDLE_TYPE( BOOL, bool);
HANDLE_TYPE( ENUM, enum);
HANDLE_TYPE( STRING, string);
#undef HANDLE_TYPE
case FieldDescriptor::CPPTYPE_MESSAGE:
// repeated_message_value is actually a RepeatedPtrField<MessageLite>,
// but MessageLite has no SpaceUsed(), so we must directly call
// RepeatedPtrFieldBase::SpaceUsedExcludingSelf() with a different type
// handler.
total_size += sizeof(*repeated_message_value) +
RepeatedMessage_SpaceUsedExcludingSelf(repeated_message_value);
break;
}
} else {
switch (cpp_type(type)) {
case FieldDescriptor::CPPTYPE_STRING:
total_size += sizeof(*string_value) +
StringSpaceUsedExcludingSelf(*string_value);
break;
case FieldDescriptor::CPPTYPE_MESSAGE:
total_size += down_cast<Message*>(message_value)->SpaceUsed();
break;
default:
// No extra storage costs for primitive types.
break;
}
}
return total_size;
}
// The Serialize*ToArray methods are only needed in the heavy library, as
// the lite library only generates SerializeWithCachedSizes.
uint8* ExtensionSet::SerializeWithCachedSizesToArray(
int start_field_number, int end_field_number,
uint8* target) const {
map<int, Extension>::const_iterator iter;
for (iter = extensions_.lower_bound(start_field_number);
iter != extensions_.end() && iter->first < end_field_number;
++iter) {
target = iter->second.SerializeFieldWithCachedSizesToArray(iter->first,
target);
}
return target;
}
uint8* ExtensionSet::SerializeMessageSetWithCachedSizesToArray(
uint8* target) const {
map<int, Extension>::const_iterator iter;
for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
target = iter->second.SerializeMessageSetItemWithCachedSizesToArray(
iter->first, target);
}
return target;
}
uint8* ExtensionSet::Extension::SerializeFieldWithCachedSizesToArray(
int number, uint8* target) const {
if (is_repeated) {
if (is_packed) {
if (cached_size == 0) return target;
target = WireFormatLite::WriteTagToArray(number,
WireFormatLite::WIRETYPE_LENGTH_DELIMITED, target);
target = WireFormatLite::WriteInt32NoTagToArray(cached_size, target);
switch (real_type(type)) {
#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE) \
case FieldDescriptor::TYPE_##UPPERCASE: \
for (int i = 0; i < repeated_##LOWERCASE##_value->size(); i++) { \
target = WireFormatLite::Write##CAMELCASE##NoTagToArray( \
repeated_##LOWERCASE##_value->Get(i), target); \
} \
break
HANDLE_TYPE( INT32, Int32, int32);
HANDLE_TYPE( INT64, Int64, int64);
HANDLE_TYPE( UINT32, UInt32, uint32);
HANDLE_TYPE( UINT64, UInt64, uint64);
HANDLE_TYPE( SINT32, SInt32, int32);
HANDLE_TYPE( SINT64, SInt64, int64);
HANDLE_TYPE( FIXED32, Fixed32, uint32);
HANDLE_TYPE( FIXED64, Fixed64, uint64);
HANDLE_TYPE(SFIXED32, SFixed32, int32);
HANDLE_TYPE(SFIXED64, SFixed64, int64);
HANDLE_TYPE( FLOAT, Float, float);
HANDLE_TYPE( DOUBLE, Double, double);
HANDLE_TYPE( BOOL, Bool, bool);
HANDLE_TYPE( ENUM, Enum, enum);
#undef HANDLE_TYPE
case WireFormatLite::TYPE_STRING:
case WireFormatLite::TYPE_BYTES:
case WireFormatLite::TYPE_GROUP:
case WireFormatLite::TYPE_MESSAGE:
GOOGLE_LOG(FATAL) << "Non-primitive types can't be packed.";
break;
}
} else {
switch (real_type(type)) {
#define HANDLE_TYPE(UPPERCASE, CAMELCASE, LOWERCASE) \
case FieldDescriptor::TYPE_##UPPERCASE: \
for (int i = 0; i < repeated_##LOWERCASE##_value->size(); i++) { \
target = WireFormatLite::Write##CAMELCASE##ToArray(number, \
repeated_##LOWERCASE##_value->Get(i), target); \
} \
break
HANDLE_TYPE( INT32, Int32, int32);
HANDLE_TYPE( INT64, Int64, int64);
HANDLE_TYPE( UINT32, UInt32, uint32);
HANDLE_TYPE( UINT64, UInt64, uint64);
HANDLE_TYPE( SINT32, SInt32, int32);
HANDLE_TYPE( SINT64, SInt64, int64);
HANDLE_TYPE( FIXED32, Fixed32, uint32);
HANDLE_TYPE( FIXED64, Fixed64, uint64);
HANDLE_TYPE(SFIXED32, SFixed32, int32);
HANDLE_TYPE(SFIXED64, SFixed64, int64);
HANDLE_TYPE( FLOAT, Float, float);
HANDLE_TYPE( DOUBLE, Double, double);
HANDLE_TYPE( BOOL, Bool, bool);
HANDLE_TYPE( STRING, String, string);
HANDLE_TYPE( BYTES, Bytes, string);
HANDLE_TYPE( ENUM, Enum, enum);
HANDLE_TYPE( GROUP, Group, message);
HANDLE_TYPE( MESSAGE, Message, message);
#undef HANDLE_TYPE
}
}
} else if (!is_cleared) {
switch (real_type(type)) {
#define HANDLE_TYPE(UPPERCASE, CAMELCASE, VALUE) \
case FieldDescriptor::TYPE_##UPPERCASE: \
target = WireFormatLite::Write##CAMELCASE##ToArray( \
number, VALUE, target); \
break
HANDLE_TYPE( INT32, Int32, int32_value);
HANDLE_TYPE( INT64, Int64, int64_value);
HANDLE_TYPE( UINT32, UInt32, uint32_value);
HANDLE_TYPE( UINT64, UInt64, uint64_value);
HANDLE_TYPE( SINT32, SInt32, int32_value);
HANDLE_TYPE( SINT64, SInt64, int64_value);
HANDLE_TYPE( FIXED32, Fixed32, uint32_value);
HANDLE_TYPE( FIXED64, Fixed64, uint64_value);
HANDLE_TYPE(SFIXED32, SFixed32, int32_value);
HANDLE_TYPE(SFIXED64, SFixed64, int64_value);
HANDLE_TYPE( FLOAT, Float, float_value);
HANDLE_TYPE( DOUBLE, Double, double_value);
HANDLE_TYPE( BOOL, Bool, bool_value);
HANDLE_TYPE( STRING, String, *string_value);
HANDLE_TYPE( BYTES, Bytes, *string_value);
HANDLE_TYPE( ENUM, Enum, enum_value);
HANDLE_TYPE( GROUP, Group, *message_value);
HANDLE_TYPE( MESSAGE, Message, *message_value);
#undef HANDLE_TYPE
}
}
return target;
}
uint8* ExtensionSet::Extension::SerializeMessageSetItemWithCachedSizesToArray(
int number,
uint8* target) const {
if (type != WireFormatLite::TYPE_MESSAGE || is_repeated) {
// Not a valid MessageSet extension, but serialize it the normal way.
GOOGLE_LOG(WARNING) << "Invalid message set extension.";
return SerializeFieldWithCachedSizesToArray(number, target);
}
if (is_cleared) return target;
// Start group.
target = io::CodedOutputStream::WriteTagToArray(
WireFormatLite::kMessageSetItemStartTag, target);
// Write type ID.
target = WireFormatLite::WriteUInt32ToArray(
WireFormatLite::kMessageSetTypeIdNumber, number, target);
// Write message.
target = WireFormatLite::WriteMessageToArray(
WireFormatLite::kMessageSetMessageNumber, *message_value, target);
// End group.
target = io::CodedOutputStream::WriteTagToArray(
WireFormatLite::kMessageSetItemEndTag, target);
return target;
}
} // namespace internal
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,642 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/extension_set.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
namespace protobuf {
namespace internal {
namespace {
// This test closely mirrors google/protobuf/compiler/cpp/unittest.cc
// except that it uses extensions rather than regular fields.
TEST(ExtensionSetTest, Defaults) {
// Check that all default values are set correctly in the initial message.
unittest::TestAllExtensions message;
TestUtil::ExpectExtensionsClear(message);
// Messages should return pointers to default instances until first use.
// (This is not checked by ExpectClear() since it is not actually true after
// the fields have been set and then cleared.)
EXPECT_EQ(&unittest::OptionalGroup_extension::default_instance(),
&message.GetExtension(unittest::optionalgroup_extension));
EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(),
&message.GetExtension(unittest::optional_nested_message_extension));
EXPECT_EQ(&unittest::ForeignMessage::default_instance(),
&message.GetExtension(
unittest::optional_foreign_message_extension));
EXPECT_EQ(&unittest_import::ImportMessage::default_instance(),
&message.GetExtension(unittest::optional_import_message_extension));
}
TEST(ExtensionSetTest, Accessors) {
// Set every field to a unique value then go back and check all those
// values.
unittest::TestAllExtensions message;
TestUtil::SetAllExtensions(&message);
TestUtil::ExpectAllExtensionsSet(message);
TestUtil::ModifyRepeatedExtensions(&message);
TestUtil::ExpectRepeatedExtensionsModified(message);
}
TEST(ExtensionSetTest, Clear) {
// Set every field to a unique value, clear the message, then check that
// it is cleared.
unittest::TestAllExtensions message;
TestUtil::SetAllExtensions(&message);
message.Clear();
TestUtil::ExpectExtensionsClear(message);
// Unlike with the defaults test, we do NOT expect that requesting embedded
// messages will return a pointer to the default instance. Instead, they
// should return the objects that were created when mutable_blah() was
// called.
EXPECT_NE(&unittest::OptionalGroup_extension::default_instance(),
&message.GetExtension(unittest::optionalgroup_extension));
EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(),
&message.GetExtension(unittest::optional_nested_message_extension));
EXPECT_NE(&unittest::ForeignMessage::default_instance(),
&message.GetExtension(
unittest::optional_foreign_message_extension));
EXPECT_NE(&unittest_import::ImportMessage::default_instance(),
&message.GetExtension(unittest::optional_import_message_extension));
// Make sure setting stuff again after clearing works. (This takes slightly
// different code paths since the objects are reused.)
TestUtil::SetAllExtensions(&message);
TestUtil::ExpectAllExtensionsSet(message);
}
TEST(ExtensionSetTest, ClearOneField) {
// Set every field to a unique value, then clear one value and insure that
// only that one value is cleared.
unittest::TestAllExtensions message;
TestUtil::SetAllExtensions(&message);
int64 original_value =
message.GetExtension(unittest::optional_int64_extension);
// Clear the field and make sure it shows up as cleared.
message.ClearExtension(unittest::optional_int64_extension);
EXPECT_FALSE(message.HasExtension(unittest::optional_int64_extension));
EXPECT_EQ(0, message.GetExtension(unittest::optional_int64_extension));
// Other adjacent fields should not be cleared.
EXPECT_TRUE(message.HasExtension(unittest::optional_int32_extension));
EXPECT_TRUE(message.HasExtension(unittest::optional_uint32_extension));
// Make sure if we set it again, then all fields are set.
message.SetExtension(unittest::optional_int64_extension, original_value);
TestUtil::ExpectAllExtensionsSet(message);
}
TEST(ExtensionSetTest, CopyFrom) {
unittest::TestAllExtensions message1, message2;
string data;
TestUtil::SetAllExtensions(&message1);
message2.CopyFrom(message1);
TestUtil::ExpectAllExtensionsSet(message2);
}
TEST(ExtensionSetTest, CopyFromUpcasted) {
unittest::TestAllExtensions message1, message2;
string data;
const Message& upcasted_message = message1;
TestUtil::SetAllExtensions(&message1);
message2.CopyFrom(upcasted_message);
TestUtil::ExpectAllExtensionsSet(message2);
}
TEST(ExtensionSetTest, SwapWithEmpty) {
unittest::TestAllExtensions message1, message2;
TestUtil::SetAllExtensions(&message1);
TestUtil::ExpectAllExtensionsSet(message1);
TestUtil::ExpectExtensionsClear(message2);
message1.Swap(&message2);
TestUtil::ExpectAllExtensionsSet(message2);
TestUtil::ExpectExtensionsClear(message1);
}
TEST(ExtensionSetTest, SwapWithSelf) {
unittest::TestAllExtensions message;
TestUtil::SetAllExtensions(&message);
TestUtil::ExpectAllExtensionsSet(message);
message.Swap(&message);
TestUtil::ExpectAllExtensionsSet(message);
}
TEST(ExtensionSetTest, SerializationToArray) {
// Serialize as TestAllExtensions and parse as TestAllTypes to insure wire
// compatibility of extensions.
//
// This checks serialization to a flat array by explicitly reserving space in
// the string and calling the generated message's
// SerializeWithCachedSizesToArray.
unittest::TestAllExtensions source;
unittest::TestAllTypes destination;
TestUtil::SetAllExtensions(&source);
int size = source.ByteSize();
string data;
data.resize(size);
uint8* target = reinterpret_cast<uint8*>(string_as_array(&data));
uint8* end = source.SerializeWithCachedSizesToArray(target);
EXPECT_EQ(size, end - target);
EXPECT_TRUE(destination.ParseFromString(data));
TestUtil::ExpectAllFieldsSet(destination);
}
TEST(ExtensionSetTest, SerializationToStream) {
// Serialize as TestAllExtensions and parse as TestAllTypes to insure wire
// compatibility of extensions.
//
// This checks serialization to an output stream by creating an array output
// stream that can only buffer 1 byte at a time - this prevents the message
// from ever jumping to the fast path, ensuring that serialization happens via
// the CodedOutputStream.
unittest::TestAllExtensions source;
unittest::TestAllTypes destination;
TestUtil::SetAllExtensions(&source);
int size = source.ByteSize();
string data;
data.resize(size);
{
io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
io::CodedOutputStream output_stream(&array_stream);
source.SerializeWithCachedSizes(&output_stream);
ASSERT_FALSE(output_stream.HadError());
}
EXPECT_TRUE(destination.ParseFromString(data));
TestUtil::ExpectAllFieldsSet(destination);
}
TEST(ExtensionSetTest, PackedSerializationToArray) {
// Serialize as TestPackedExtensions and parse as TestPackedTypes to insure
// wire compatibility of extensions.
//
// This checks serialization to a flat array by explicitly reserving space in
// the string and calling the generated message's
// SerializeWithCachedSizesToArray.
unittest::TestPackedExtensions source;
unittest::TestPackedTypes destination;
TestUtil::SetPackedExtensions(&source);
int size = source.ByteSize();
string data;
data.resize(size);
uint8* target = reinterpret_cast<uint8*>(string_as_array(&data));
uint8* end = source.SerializeWithCachedSizesToArray(target);
EXPECT_EQ(size, end - target);
EXPECT_TRUE(destination.ParseFromString(data));
TestUtil::ExpectPackedFieldsSet(destination);
}
TEST(ExtensionSetTest, PackedSerializationToStream) {
// Serialize as TestPackedExtensions and parse as TestPackedTypes to insure
// wire compatibility of extensions.
//
// This checks serialization to an output stream by creating an array output
// stream that can only buffer 1 byte at a time - this prevents the message
// from ever jumping to the fast path, ensuring that serialization happens via
// the CodedOutputStream.
unittest::TestPackedExtensions source;
unittest::TestPackedTypes destination;
TestUtil::SetPackedExtensions(&source);
int size = source.ByteSize();
string data;
data.resize(size);
{
io::ArrayOutputStream array_stream(string_as_array(&data), size, 1);
io::CodedOutputStream output_stream(&array_stream);
source.SerializeWithCachedSizes(&output_stream);
ASSERT_FALSE(output_stream.HadError());
}
EXPECT_TRUE(destination.ParseFromString(data));
TestUtil::ExpectPackedFieldsSet(destination);
}
TEST(ExtensionSetTest, Parsing) {
// Serialize as TestAllTypes and parse as TestAllExtensions.
unittest::TestAllTypes source;
unittest::TestAllExtensions destination;
string data;
TestUtil::SetAllFields(&source);
source.SerializeToString(&data);
EXPECT_TRUE(destination.ParseFromString(data));
TestUtil::ExpectAllExtensionsSet(destination);
}
TEST(ExtensionSetTest, PackedParsing) {
// Serialize as TestPackedTypes and parse as TestPackedExtensions.
unittest::TestPackedTypes source;
unittest::TestPackedExtensions destination;
string data;
TestUtil::SetPackedFields(&source);
source.SerializeToString(&data);
EXPECT_TRUE(destination.ParseFromString(data));
TestUtil::ExpectPackedExtensionsSet(destination);
}
TEST(ExtensionSetTest, IsInitialized) {
// Test that IsInitialized() returns false if required fields in nested
// extensions are missing.
unittest::TestAllExtensions message;
EXPECT_TRUE(message.IsInitialized());
message.MutableExtension(unittest::TestRequired::single);
EXPECT_FALSE(message.IsInitialized());
message.MutableExtension(unittest::TestRequired::single)->set_a(1);
EXPECT_FALSE(message.IsInitialized());
message.MutableExtension(unittest::TestRequired::single)->set_b(2);
EXPECT_FALSE(message.IsInitialized());
message.MutableExtension(unittest::TestRequired::single)->set_c(3);
EXPECT_TRUE(message.IsInitialized());
message.AddExtension(unittest::TestRequired::multi);
EXPECT_FALSE(message.IsInitialized());
message.MutableExtension(unittest::TestRequired::multi, 0)->set_a(1);
EXPECT_FALSE(message.IsInitialized());
message.MutableExtension(unittest::TestRequired::multi, 0)->set_b(2);
EXPECT_FALSE(message.IsInitialized());
message.MutableExtension(unittest::TestRequired::multi, 0)->set_c(3);
EXPECT_TRUE(message.IsInitialized());
}
TEST(ExtensionSetTest, MutableString) {
// Test the mutable string accessors.
unittest::TestAllExtensions message;
message.MutableExtension(unittest::optional_string_extension)->assign("foo");
EXPECT_TRUE(message.HasExtension(unittest::optional_string_extension));
EXPECT_EQ("foo", message.GetExtension(unittest::optional_string_extension));
message.AddExtension(unittest::repeated_string_extension)->assign("bar");
ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_string_extension));
EXPECT_EQ("bar",
message.GetExtension(unittest::repeated_string_extension, 0));
}
TEST(ExtensionSetTest, SpaceUsedExcludingSelf) {
// Scalar primitive extensions should increase the extension set size by a
// minimum of the size of the primitive type.
#define TEST_SCALAR_EXTENSIONS_SPACE_USED(type, value) \
do { \
unittest::TestAllExtensions message; \
const int base_size = message.SpaceUsed(); \
message.SetExtension(unittest::optional_##type##_extension, value); \
int min_expected_size = base_size + \
sizeof(message.GetExtension(unittest::optional_##type##_extension)); \
EXPECT_LE(min_expected_size, message.SpaceUsed()); \
} while (0)
TEST_SCALAR_EXTENSIONS_SPACE_USED(int32 , 101);
TEST_SCALAR_EXTENSIONS_SPACE_USED(int64 , 102);
TEST_SCALAR_EXTENSIONS_SPACE_USED(uint32 , 103);
TEST_SCALAR_EXTENSIONS_SPACE_USED(uint64 , 104);
TEST_SCALAR_EXTENSIONS_SPACE_USED(sint32 , 105);
TEST_SCALAR_EXTENSIONS_SPACE_USED(sint64 , 106);
TEST_SCALAR_EXTENSIONS_SPACE_USED(fixed32 , 107);
TEST_SCALAR_EXTENSIONS_SPACE_USED(fixed64 , 108);
TEST_SCALAR_EXTENSIONS_SPACE_USED(sfixed32, 109);
TEST_SCALAR_EXTENSIONS_SPACE_USED(sfixed64, 110);
TEST_SCALAR_EXTENSIONS_SPACE_USED(float , 111);
TEST_SCALAR_EXTENSIONS_SPACE_USED(double , 112);
TEST_SCALAR_EXTENSIONS_SPACE_USED(bool , true);
#undef TEST_SCALAR_EXTENSIONS_SPACE_USED
{
unittest::TestAllExtensions message;
const int base_size = message.SpaceUsed();
message.SetExtension(unittest::optional_nested_enum_extension,
unittest::TestAllTypes::FOO);
int min_expected_size = base_size +
sizeof(message.GetExtension(unittest::optional_nested_enum_extension));
EXPECT_LE(min_expected_size, message.SpaceUsed());
}
{
// Strings may cause extra allocations depending on their length; ensure
// that gets included as well.
unittest::TestAllExtensions message;
const int base_size = message.SpaceUsed();
const string s("this is a fairly large string that will cause some "
"allocation in order to store it in the extension");
message.SetExtension(unittest::optional_string_extension, s);
int min_expected_size = base_size + s.length();
EXPECT_LE(min_expected_size, message.SpaceUsed());
}
{
// Messages also have additional allocation that need to be counted.
unittest::TestAllExtensions message;
const int base_size = message.SpaceUsed();
unittest::ForeignMessage foreign;
foreign.set_c(42);
message.MutableExtension(unittest::optional_foreign_message_extension)->
CopyFrom(foreign);
int min_expected_size = base_size + foreign.SpaceUsed();
EXPECT_LE(min_expected_size, message.SpaceUsed());
}
// Repeated primitive extensions will increase space used by at least a
// RepeatedField<T>, and will cause additional allocations when the array
// gets too big for the initial space.
// This macro:
// - Adds a value to the repeated extension, then clears it, establishing
// the base size.
// - Adds a small number of values, testing that it doesn't increase the
// SpaceUsed()
// - Adds a large number of values (requiring allocation in the repeated
// field), and ensures that that allocation is included in SpaceUsed()
#define TEST_REPEATED_EXTENSIONS_SPACE_USED(type, cpptype, value) \
do { \
unittest::TestAllExtensions message; \
const int base_size = message.SpaceUsed(); \
int min_expected_size = sizeof(RepeatedField<cpptype>) + base_size; \
message.AddExtension(unittest::repeated_##type##_extension, value); \
message.ClearExtension(unittest::repeated_##type##_extension); \
const int empty_repeated_field_size = message.SpaceUsed(); \
EXPECT_LE(min_expected_size, empty_repeated_field_size) << #type; \
message.AddExtension(unittest::repeated_##type##_extension, value); \
message.AddExtension(unittest::repeated_##type##_extension, value); \
EXPECT_EQ(empty_repeated_field_size, message.SpaceUsed()) << #type; \
message.ClearExtension(unittest::repeated_##type##_extension); \
for (int i = 0; i < 16; ++i) { \
message.AddExtension(unittest::repeated_##type##_extension, value); \
} \
int expected_size = sizeof(cpptype) * 16 + empty_repeated_field_size; \
EXPECT_EQ(expected_size, message.SpaceUsed()) << #type; \
} while (0)
TEST_REPEATED_EXTENSIONS_SPACE_USED(int32 , int32 , 101);
TEST_REPEATED_EXTENSIONS_SPACE_USED(int64 , int64 , 102);
TEST_REPEATED_EXTENSIONS_SPACE_USED(uint32 , uint32, 103);
TEST_REPEATED_EXTENSIONS_SPACE_USED(uint64 , uint64, 104);
TEST_REPEATED_EXTENSIONS_SPACE_USED(sint32 , int32 , 105);
TEST_REPEATED_EXTENSIONS_SPACE_USED(sint64 , int64 , 106);
TEST_REPEATED_EXTENSIONS_SPACE_USED(fixed32 , uint32, 107);
TEST_REPEATED_EXTENSIONS_SPACE_USED(fixed64 , uint64, 108);
TEST_REPEATED_EXTENSIONS_SPACE_USED(sfixed32, int32 , 109);
TEST_REPEATED_EXTENSIONS_SPACE_USED(sfixed64, int64 , 110);
TEST_REPEATED_EXTENSIONS_SPACE_USED(float , float , 111);
TEST_REPEATED_EXTENSIONS_SPACE_USED(double , double, 112);
TEST_REPEATED_EXTENSIONS_SPACE_USED(bool , bool , true);
TEST_REPEATED_EXTENSIONS_SPACE_USED(nested_enum, int,
unittest::TestAllTypes::FOO);
#undef TEST_REPEATED_EXTENSIONS_SPACE_USED
// Repeated strings
{
unittest::TestAllExtensions message;
const int base_size = message.SpaceUsed();
int min_expected_size = sizeof(RepeatedPtrField<string>) + base_size;
const string value(256, 'x');
// Once items are allocated, they may stick around even when cleared so
// without the hardcore memory management accessors there isn't a notion of
// the empty repeated field memory usage as there is with primitive types.
for (int i = 0; i < 16; ++i) {
message.AddExtension(unittest::repeated_string_extension, value);
}
min_expected_size += (sizeof(value) + value.size()) * 16;
EXPECT_LE(min_expected_size, message.SpaceUsed());
}
// Repeated messages
{
unittest::TestAllExtensions message;
const int base_size = message.SpaceUsed();
int min_expected_size = sizeof(RepeatedPtrField<unittest::ForeignMessage>) +
base_size;
unittest::ForeignMessage prototype;
prototype.set_c(2);
for (int i = 0; i < 16; ++i) {
message.AddExtension(unittest::repeated_foreign_message_extension)->
CopyFrom(prototype);
}
min_expected_size += 16 * prototype.SpaceUsed();
EXPECT_LE(min_expected_size, message.SpaceUsed());
}
}
#ifdef GTEST_HAS_DEATH_TEST
TEST(ExtensionSetTest, InvalidEnumDeath) {
unittest::TestAllExtensions message;
EXPECT_DEBUG_DEATH(
message.SetExtension(unittest::optional_foreign_enum_extension,
static_cast<unittest::ForeignEnum>(53)),
"IsValid");
}
#endif // GTEST_HAS_DEATH_TEST
TEST(ExtensionSetTest, DynamicExtensions) {
// Test adding a dynamic extension to a compiled-in message object.
FileDescriptorProto dynamic_proto;
dynamic_proto.set_name("dynamic_extensions_test.proto");
dynamic_proto.add_dependency(
unittest::TestAllExtensions::descriptor()->file()->name());
dynamic_proto.set_package("dynamic_extensions");
// Copy the fields and nested types from TestDynamicExtensions into our new
// proto, converting the fields into extensions.
const Descriptor* template_descriptor =
unittest::TestDynamicExtensions::descriptor();
DescriptorProto template_descriptor_proto;
template_descriptor->CopyTo(&template_descriptor_proto);
dynamic_proto.mutable_message_type()->MergeFrom(
template_descriptor_proto.nested_type());
dynamic_proto.mutable_enum_type()->MergeFrom(
template_descriptor_proto.enum_type());
dynamic_proto.mutable_extension()->MergeFrom(
template_descriptor_proto.field());
// For each extension that we added...
for (int i = 0; i < dynamic_proto.extension_size(); i++) {
// Set its extendee to TestAllExtensions.
FieldDescriptorProto* extension = dynamic_proto.mutable_extension(i);
extension->set_extendee(
unittest::TestAllExtensions::descriptor()->full_name());
// If the field refers to one of the types nested in TestDynamicExtensions,
// make it refer to the type in our dynamic proto instead.
string prefix = "." + template_descriptor->full_name() + ".";
if (extension->has_type_name()) {
string* type_name = extension->mutable_type_name();
if (HasPrefixString(*type_name, prefix)) {
type_name->replace(0, prefix.size(), ".dynamic_extensions.");
}
}
}
// Now build the file, using the generated pool as an underlay.
DescriptorPool dynamic_pool(DescriptorPool::generated_pool());
const FileDescriptor* file = dynamic_pool.BuildFile(dynamic_proto);
ASSERT_TRUE(file != NULL);
DynamicMessageFactory dynamic_factory(&dynamic_pool);
dynamic_factory.SetDelegateToGeneratedFactory(true);
// Construct a message that we can parse with the extensions we defined.
// Since the extensions were based off of the fields of TestDynamicExtensions,
// we can use that message to create this test message.
string data;
{
unittest::TestDynamicExtensions message;
message.set_scalar_extension(123);
message.set_enum_extension(unittest::FOREIGN_BAR);
message.set_dynamic_enum_extension(
unittest::TestDynamicExtensions::DYNAMIC_BAZ);
message.mutable_message_extension()->set_c(456);
message.mutable_dynamic_message_extension()->set_dynamic_field(789);
message.add_repeated_extension("foo");
message.add_repeated_extension("bar");
message.add_packed_extension(12);
message.add_packed_extension(-34);
message.add_packed_extension(56);
message.add_packed_extension(-78);
// Also add some unknown fields.
// An unknown enum value (for a known field).
message.mutable_unknown_fields()->AddVarint(
unittest::TestDynamicExtensions::kDynamicEnumExtensionFieldNumber,
12345);
// A regular unknown field.
message.mutable_unknown_fields()->AddLengthDelimited(54321, "unknown");
message.SerializeToString(&data);
}
// Now we can parse this using our dynamic extension definitions...
unittest::TestAllExtensions message;
{
io::ArrayInputStream raw_input(data.data(), data.size());
io::CodedInputStream input(&raw_input);
input.SetExtensionRegistry(&dynamic_pool, &dynamic_factory);
ASSERT_TRUE(message.ParseFromCodedStream(&input));
ASSERT_TRUE(input.ConsumedEntireMessage());
}
// Can we print it?
EXPECT_EQ(
"[dynamic_extensions.scalar_extension]: 123\n"
"[dynamic_extensions.enum_extension]: FOREIGN_BAR\n"
"[dynamic_extensions.dynamic_enum_extension]: DYNAMIC_BAZ\n"
"[dynamic_extensions.message_extension] {\n"
" c: 456\n"
"}\n"
"[dynamic_extensions.dynamic_message_extension] {\n"
" dynamic_field: 789\n"
"}\n"
"[dynamic_extensions.repeated_extension]: \"foo\"\n"
"[dynamic_extensions.repeated_extension]: \"bar\"\n"
"[dynamic_extensions.packed_extension]: 12\n"
"[dynamic_extensions.packed_extension]: -34\n"
"[dynamic_extensions.packed_extension]: 56\n"
"[dynamic_extensions.packed_extension]: -78\n"
"2002: 12345\n"
"54321: \"unknown\"\n",
message.DebugString());
// Can we serialize it?
// (Don't use EXPECT_EQ because we don't want to dump raw binary data to the
// terminal on failure.)
EXPECT_TRUE(message.SerializeAsString() == data);
// What if we parse using the reflection-based parser?
{
unittest::TestAllExtensions message2;
io::ArrayInputStream raw_input(data.data(), data.size());
io::CodedInputStream input(&raw_input);
input.SetExtensionRegistry(&dynamic_pool, &dynamic_factory);
ASSERT_TRUE(WireFormat::ParseAndMergePartial(&input, &message2));
ASSERT_TRUE(input.ConsumedEntireMessage());
EXPECT_EQ(message.DebugString(), message2.DebugString());
}
// Are the embedded generated types actually using the generated objects?
{
const FieldDescriptor* message_extension =
file->FindExtensionByName("message_extension");
ASSERT_TRUE(message_extension != NULL);
const Message& sub_message =
message.GetReflection()->GetMessage(message, message_extension);
const unittest::ForeignMessage* typed_sub_message =
dynamic_cast<const unittest::ForeignMessage*>(&sub_message);
ASSERT_TRUE(typed_sub_message != NULL);
EXPECT_EQ(456, typed_sub_message->c());
}
// What does GetMessage() return for the embedded dynamic type if it isn't
// present?
{
const FieldDescriptor* dynamic_message_extension =
file->FindExtensionByName("dynamic_message_extension");
ASSERT_TRUE(dynamic_message_extension != NULL);
const Message& parent = unittest::TestAllExtensions::default_instance();
const Message& sub_message =
parent.GetReflection()->GetMessage(parent, dynamic_message_extension,
&dynamic_factory);
const Message* prototype =
dynamic_factory.GetPrototype(dynamic_message_extension->message_type());
EXPECT_EQ(prototype, &sub_message);
}
}
} // namespace
} // namespace internal
} // namespace protobuf
} // namespace google

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,424 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This header is logically internal, but is made public because it is used
// from protocol-compiler-generated code, which may reside in other components.
#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__
#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__
#include <string>
#include <vector>
#include <google/protobuf/message.h>
#include <google/protobuf/unknown_field_set.h>
namespace google {
namespace protobuf {
class DescriptorPool;
// Generated code needs these to have been forward-declared. Easier to do it
// here than to print them inside every .pb.h file.
class FileDescriptor;
class EnumDescriptor;
}
namespace protobuf {
namespace internal {
// Defined in this file.
class GeneratedMessageReflection;
// Defined in other files.
class ExtensionSet; // extension_set.h
// THIS CLASS IS NOT INTENDED FOR DIRECT USE. It is intended for use
// by generated code. This class is just a big hack that reduces code
// size.
//
// A GeneratedMessageReflection is an implementation of Reflection
// which expects all fields to be backed by simple variables located in
// memory. The locations are given using a base pointer and a set of
// offsets.
//
// It is required that the user represents fields of each type in a standard
// way, so that GeneratedMessageReflection can cast the void* pointer to
// the appropriate type. For primitive fields and string fields, each field
// should be represented using the obvious C++ primitive type. Enums and
// Messages are different:
// - Singular Message fields are stored as a pointer to a Message. These
// should start out NULL, except for in the default instance where they
// should start out pointing to other default instances.
// - Enum fields are stored as an int. This int must always contain
// a valid value, such that EnumDescriptor::FindValueByNumber() would
// not return NULL.
// - Repeated fields are stored as RepeatedFields or RepeatedPtrFields
// of whatever type the individual field would be. Strings and
// Messages use RepeatedPtrFields while everything else uses
// RepeatedFields.
class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection {
public:
// Constructs a GeneratedMessageReflection.
// Parameters:
// descriptor: The descriptor for the message type being implemented.
// default_instance: The default instance of the message. This is only
// used to obtain pointers to default instances of embedded
// messages, which GetMessage() will return if the particular
// sub-message has not been initialized yet. (Thus, all
// embedded message fields *must* have non-NULL pointers
// in the default instance.)
// offsets: An array of ints giving the byte offsets, relative to
// the start of the message object, of each field. These can
// be computed at compile time using the
// GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET() macro, defined
// below.
// has_bits_offset: Offset in the message of an array of uint32s of size
// descriptor->field_count()/32, rounded up. This is a
// bitfield where each bit indicates whether or not the
// corresponding field of the message has been initialized.
// The bit for field index i is obtained by the expression:
// has_bits[i / 32] & (1 << (i % 32))
// unknown_fields_offset: Offset in the message of the UnknownFieldSet for
// the message.
// extensions_offset: Offset in the message of the ExtensionSet for the
// message, or -1 if the message type has no extension
// ranges.
// pool: DescriptorPool to search for extension definitions. Only
// used by FindKnownExtensionByName() and
// FindKnownExtensionByNumber().
// factory: MessageFactory to use to construct extension messages.
// object_size: The size of a message object of this type, as measured
// by sizeof().
GeneratedMessageReflection(const Descriptor* descriptor,
const Message* default_instance,
const int offsets[],
int has_bits_offset,
int unknown_fields_offset,
int extensions_offset,
const DescriptorPool* pool,
MessageFactory* factory,
int object_size);
~GeneratedMessageReflection();
// implements Reflection -------------------------------------------
const UnknownFieldSet& GetUnknownFields(const Message& message) const;
UnknownFieldSet* MutableUnknownFields(Message* message) const;
int SpaceUsed(const Message& message) const;
bool HasField(const Message& message, const FieldDescriptor* field) const;
int FieldSize(const Message& message, const FieldDescriptor* field) const;
void ClearField(Message* message, const FieldDescriptor* field) const;
void RemoveLast(Message* message, const FieldDescriptor* field) const;
void Swap(Message* message1, Message* message2) const;
void SwapElements(Message* message, const FieldDescriptor* field,
int index1, int index2) const;
void ListFields(const Message& message,
vector<const FieldDescriptor*>* output) const;
int32 GetInt32 (const Message& message,
const FieldDescriptor* field) const;
int64 GetInt64 (const Message& message,
const FieldDescriptor* field) const;
uint32 GetUInt32(const Message& message,
const FieldDescriptor* field) const;
uint64 GetUInt64(const Message& message,
const FieldDescriptor* field) const;
float GetFloat (const Message& message,
const FieldDescriptor* field) const;
double GetDouble(const Message& message,
const FieldDescriptor* field) const;
bool GetBool (const Message& message,
const FieldDescriptor* field) const;
string GetString(const Message& message,
const FieldDescriptor* field) const;
const string& GetStringReference(const Message& message,
const FieldDescriptor* field,
string* scratch) const;
const EnumValueDescriptor* GetEnum(const Message& message,
const FieldDescriptor* field) const;
const Message& GetMessage(const Message& message,
const FieldDescriptor* field,
MessageFactory* factory = NULL) const;
void SetInt32 (Message* message,
const FieldDescriptor* field, int32 value) const;
void SetInt64 (Message* message,
const FieldDescriptor* field, int64 value) const;
void SetUInt32(Message* message,
const FieldDescriptor* field, uint32 value) const;
void SetUInt64(Message* message,
const FieldDescriptor* field, uint64 value) const;
void SetFloat (Message* message,
const FieldDescriptor* field, float value) const;
void SetDouble(Message* message,
const FieldDescriptor* field, double value) const;
void SetBool (Message* message,
const FieldDescriptor* field, bool value) const;
void SetString(Message* message,
const FieldDescriptor* field,
const string& value) const;
void SetEnum (Message* message, const FieldDescriptor* field,
const EnumValueDescriptor* value) const;
Message* MutableMessage(Message* message, const FieldDescriptor* field,
MessageFactory* factory = NULL) const;
int32 GetRepeatedInt32 (const Message& message,
const FieldDescriptor* field, int index) const;
int64 GetRepeatedInt64 (const Message& message,
const FieldDescriptor* field, int index) const;
uint32 GetRepeatedUInt32(const Message& message,
const FieldDescriptor* field, int index) const;
uint64 GetRepeatedUInt64(const Message& message,
const FieldDescriptor* field, int index) const;
float GetRepeatedFloat (const Message& message,
const FieldDescriptor* field, int index) const;
double GetRepeatedDouble(const Message& message,
const FieldDescriptor* field, int index) const;
bool GetRepeatedBool (const Message& message,
const FieldDescriptor* field, int index) const;
string GetRepeatedString(const Message& message,
const FieldDescriptor* field, int index) const;
const string& GetRepeatedStringReference(const Message& message,
const FieldDescriptor* field,
int index, string* scratch) const;
const EnumValueDescriptor* GetRepeatedEnum(const Message& message,
const FieldDescriptor* field,
int index) const;
const Message& GetRepeatedMessage(const Message& message,
const FieldDescriptor* field,
int index) const;
// Set the value of a field.
void SetRepeatedInt32 (Message* message,
const FieldDescriptor* field, int index, int32 value) const;
void SetRepeatedInt64 (Message* message,
const FieldDescriptor* field, int index, int64 value) const;
void SetRepeatedUInt32(Message* message,
const FieldDescriptor* field, int index, uint32 value) const;
void SetRepeatedUInt64(Message* message,
const FieldDescriptor* field, int index, uint64 value) const;
void SetRepeatedFloat (Message* message,
const FieldDescriptor* field, int index, float value) const;
void SetRepeatedDouble(Message* message,
const FieldDescriptor* field, int index, double value) const;
void SetRepeatedBool (Message* message,
const FieldDescriptor* field, int index, bool value) const;
void SetRepeatedString(Message* message,
const FieldDescriptor* field, int index,
const string& value) const;
void SetRepeatedEnum(Message* message, const FieldDescriptor* field,
int index, const EnumValueDescriptor* value) const;
// Get a mutable pointer to a field with a message type.
Message* MutableRepeatedMessage(Message* message,
const FieldDescriptor* field,
int index) const;
void AddInt32 (Message* message,
const FieldDescriptor* field, int32 value) const;
void AddInt64 (Message* message,
const FieldDescriptor* field, int64 value) const;
void AddUInt32(Message* message,
const FieldDescriptor* field, uint32 value) const;
void AddUInt64(Message* message,
const FieldDescriptor* field, uint64 value) const;
void AddFloat (Message* message,
const FieldDescriptor* field, float value) const;
void AddDouble(Message* message,
const FieldDescriptor* field, double value) const;
void AddBool (Message* message,
const FieldDescriptor* field, bool value) const;
void AddString(Message* message,
const FieldDescriptor* field, const string& value) const;
void AddEnum(Message* message,
const FieldDescriptor* field,
const EnumValueDescriptor* value) const;
Message* AddMessage(Message* message, const FieldDescriptor* field,
MessageFactory* factory = NULL) const;
const FieldDescriptor* FindKnownExtensionByName(const string& name) const;
const FieldDescriptor* FindKnownExtensionByNumber(int number) const;
private:
friend class GeneratedMessage;
const Descriptor* descriptor_;
const Message* default_instance_;
const int* offsets_;
int has_bits_offset_;
int unknown_fields_offset_;
int extensions_offset_;
int object_size_;
const DescriptorPool* descriptor_pool_;
MessageFactory* message_factory_;
template <typename Type>
inline const Type& GetRaw(const Message& message,
const FieldDescriptor* field) const;
template <typename Type>
inline Type* MutableRaw(Message* message,
const FieldDescriptor* field) const;
template <typename Type>
inline const Type& DefaultRaw(const FieldDescriptor* field) const;
inline const Message* GetMessagePrototype(const FieldDescriptor* field) const;
inline const uint32* GetHasBits(const Message& message) const;
inline uint32* MutableHasBits(Message* message) const;
inline const ExtensionSet& GetExtensionSet(const Message& message) const;
inline ExtensionSet* MutableExtensionSet(Message* message) const;
inline bool HasBit(const Message& message,
const FieldDescriptor* field) const;
inline void SetBit(Message* message,
const FieldDescriptor* field) const;
inline void ClearBit(Message* message,
const FieldDescriptor* field) const;
template <typename Type>
inline const Type& GetField(const Message& message,
const FieldDescriptor* field) const;
template <typename Type>
inline void SetField(Message* message,
const FieldDescriptor* field, const Type& value) const;
template <typename Type>
inline Type* MutableField(Message* message,
const FieldDescriptor* field) const;
template <typename Type>
inline const Type& GetRepeatedField(const Message& message,
const FieldDescriptor* field,
int index) const;
template <typename Type>
inline const Type& GetRepeatedPtrField(const Message& message,
const FieldDescriptor* field,
int index) const;
template <typename Type>
inline void SetRepeatedField(Message* message,
const FieldDescriptor* field, int index,
Type value) const;
template <typename Type>
inline Type* MutableRepeatedField(Message* message,
const FieldDescriptor* field,
int index) const;
template <typename Type>
inline void AddField(Message* message,
const FieldDescriptor* field, const Type& value) const;
template <typename Type>
inline Type* AddField(Message* message,
const FieldDescriptor* field) const;
int GetExtensionNumberOrDie(const Descriptor* type) const;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratedMessageReflection);
};
// Returns the offset of the given field within the given aggregate type.
// This is equivalent to the ANSI C offsetof() macro. However, according
// to the C++ standard, offsetof() only works on POD types, and GCC
// enforces this requirement with a warning. In practice, this rule is
// unnecessarily strict; there is probably no compiler or platform on
// which the offsets of the direct fields of a class are non-constant.
// Fields inherited from superclasses *can* have non-constant offsets,
// but that's not what this macro will be used for.
//
// Note that we calculate relative to the pointer value 16 here since if we
// just use zero, GCC complains about dereferencing a NULL pointer. We
// choose 16 rather than some other number just in case the compiler would
// be confused by an unaligned pointer.
#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TYPE, FIELD) \
static_cast<int>( \
reinterpret_cast<const char*>( \
&reinterpret_cast<const TYPE*>(16)->FIELD) - \
reinterpret_cast<const char*>(16))
// There are some places in proto2 where dynamic_cast would be useful as an
// optimization. For example, take Message::MergeFrom(const Message& other).
// For a given generated message FooMessage, we generate these two methods:
// void MergeFrom(const FooMessage& other);
// void MergeFrom(const Message& other);
// The former method can be implemented directly in terms of FooMessage's
// inline accessors, but the latter method must work with the reflection
// interface. However, if the parameter to the latter method is actually of
// type FooMessage, then we'd like to be able to just call the other method
// as an optimization. So, we use dynamic_cast to check this.
//
// That said, dynamic_cast requires RTTI, which many people like to disable
// for performance and code size reasons. When RTTI is not available, we
// still need to produce correct results. So, in this case we have to fall
// back to using reflection, which is what we would have done anyway if the
// objects were not of the exact same class.
//
// dynamic_cast_if_available() implements this logic. If RTTI is
// enabled, it does a dynamic_cast. If RTTI is disabled, it just returns
// NULL.
//
// If you need to compile without RTTI, simply #define GOOGLE_PROTOBUF_NO_RTTI.
// On MSVC, this should be detected automatically.
template<typename To, typename From>
inline To dynamic_cast_if_available(From from) {
#if defined(GOOGLE_PROTOBUF_NO_RTTI) || (defined(_MSC_VER)&&!defined(_CPPRTTI))
return NULL;
#else
return dynamic_cast<To>(from);
#endif
}
// Helper for EnumType_Parse functions: try to parse the string 'name' as an
// enum name of the given type, returning true and filling in value on success,
// or returning false and leaving value unchanged on failure.
LIBPROTOBUF_EXPORT bool ParseNamedEnum(const EnumDescriptor* descriptor,
const string& name,
int* value);
template<typename EnumType>
bool ParseNamedEnum(const EnumDescriptor* descriptor,
const string& name,
EnumType* value) {
int tmp;
if (!ParseNamedEnum(descriptor, name, &tmp)) return false;
*value = static_cast<EnumType>(tmp);
return true;
}
// Just a wrapper around printing the name of a value. The main point of this
// function is not to be inlined, so that you can do this without including
// descriptor.h.
LIBPROTOBUF_EXPORT const string& NameOfEnum(const EnumDescriptor* descriptor, int value);
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__

View file

@ -0,0 +1,384 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// To test GeneratedMessageReflection, we actually let the protocol compiler
// generate a full protocol message implementation and then test its
// reflection interface. This is much easier and more maintainable than
// trying to create our own Message class for GeneratedMessageReflection
// to wrap.
//
// The tests here closely mirror some of the tests in
// compiler/cpp/unittest, except using the reflection interface
// rather than generated accessors.
#include <google/protobuf/generated_message_reflection.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace {
// Shorthand to get a FieldDescriptor for a field of unittest::TestAllTypes.
const FieldDescriptor* F(const string& name) {
const FieldDescriptor* result =
unittest::TestAllTypes::descriptor()->FindFieldByName(name);
GOOGLE_CHECK(result != NULL);
return result;
}
TEST(GeneratedMessageReflectionTest, Defaults) {
// Check that all default values are set correctly in the initial message.
unittest::TestAllTypes message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllTypes::descriptor());
reflection_tester.ExpectClearViaReflection(message);
const Reflection* reflection = message.GetReflection();
// Messages should return pointers to default instances until first use.
// (This is not checked by ExpectClear() since it is not actually true after
// the fields have been set and then cleared.)
EXPECT_EQ(&unittest::TestAllTypes::OptionalGroup::default_instance(),
&reflection->GetMessage(message, F("optionalgroup")));
EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(),
&reflection->GetMessage(message, F("optional_nested_message")));
EXPECT_EQ(&unittest::ForeignMessage::default_instance(),
&reflection->GetMessage(message, F("optional_foreign_message")));
EXPECT_EQ(&unittest_import::ImportMessage::default_instance(),
&reflection->GetMessage(message, F("optional_import_message")));
}
TEST(GeneratedMessageReflectionTest, Accessors) {
// Set every field to a unique value then go back and check all those
// values.
unittest::TestAllTypes message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllTypes::descriptor());
reflection_tester.SetAllFieldsViaReflection(&message);
TestUtil::ExpectAllFieldsSet(message);
reflection_tester.ExpectAllFieldsSetViaReflection(message);
reflection_tester.ModifyRepeatedFieldsViaReflection(&message);
TestUtil::ExpectRepeatedFieldsModified(message);
}
TEST(GeneratedMessageReflectionTest, GetStringReference) {
// Test that GetStringReference() returns the underlying string when it is
// a normal string field.
unittest::TestAllTypes message;
message.set_optional_string("foo");
message.add_repeated_string("foo");
const Reflection* reflection = message.GetReflection();
string scratch;
EXPECT_EQ(&message.optional_string(),
&reflection->GetStringReference(message, F("optional_string"), &scratch))
<< "For simple string fields, GetStringReference() should return a "
"reference to the underlying string.";
EXPECT_EQ(&message.repeated_string(0),
&reflection->GetRepeatedStringReference(message, F("repeated_string"),
0, &scratch))
<< "For simple string fields, GetRepeatedStringReference() should return "
"a reference to the underlying string.";
}
TEST(GeneratedMessageReflectionTest, DefaultsAfterClear) {
// Check that after setting all fields and then clearing, getting an
// embedded message does NOT return the default instance.
unittest::TestAllTypes message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllTypes::descriptor());
TestUtil::SetAllFields(&message);
message.Clear();
const Reflection* reflection = message.GetReflection();
EXPECT_NE(&unittest::TestAllTypes::OptionalGroup::default_instance(),
&reflection->GetMessage(message, F("optionalgroup")));
EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(),
&reflection->GetMessage(message, F("optional_nested_message")));
EXPECT_NE(&unittest::ForeignMessage::default_instance(),
&reflection->GetMessage(message, F("optional_foreign_message")));
EXPECT_NE(&unittest_import::ImportMessage::default_instance(),
&reflection->GetMessage(message, F("optional_import_message")));
}
TEST(GeneratedMessageReflectionTest, Swap) {
unittest::TestAllTypes message1;
unittest::TestAllTypes message2;
TestUtil::SetAllFields(&message1);
const Reflection* reflection = message1.GetReflection();
reflection->Swap(&message1, &message2);
TestUtil::ExpectClear(message1);
TestUtil::ExpectAllFieldsSet(message2);
}
TEST(GeneratedMessageReflectionTest, SwapWithBothSet) {
unittest::TestAllTypes message1;
unittest::TestAllTypes message2;
TestUtil::SetAllFields(&message1);
TestUtil::SetAllFields(&message2);
TestUtil::ModifyRepeatedFields(&message2);
const Reflection* reflection = message1.GetReflection();
reflection->Swap(&message1, &message2);
TestUtil::ExpectRepeatedFieldsModified(message1);
TestUtil::ExpectAllFieldsSet(message2);
message1.set_optional_int32(532819);
reflection->Swap(&message1, &message2);
EXPECT_EQ(532819, message2.optional_int32());
}
TEST(GeneratedMessageReflectionTest, SwapExtensions) {
unittest::TestAllExtensions message1;
unittest::TestAllExtensions message2;
TestUtil::SetAllExtensions(&message1);
const Reflection* reflection = message1.GetReflection();
reflection->Swap(&message1, &message2);
TestUtil::ExpectExtensionsClear(message1);
TestUtil::ExpectAllExtensionsSet(message2);
}
TEST(GeneratedMessageReflectionTest, SwapUnknown) {
unittest::TestEmptyMessage message1, message2;
message1.mutable_unknown_fields()->AddVarint(1234, 1);
EXPECT_EQ(1, message1.unknown_fields().field_count());
EXPECT_EQ(0, message2.unknown_fields().field_count());
const Reflection* reflection = message1.GetReflection();
reflection->Swap(&message1, &message2);
EXPECT_EQ(0, message1.unknown_fields().field_count());
EXPECT_EQ(1, message2.unknown_fields().field_count());
}
TEST(GeneratedMessageReflectionTest, RemoveLast) {
unittest::TestAllTypes message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllTypes::descriptor());
TestUtil::SetAllFields(&message);
reflection_tester.RemoveLastRepeatedsViaReflection(&message);
TestUtil::ExpectLastRepeatedsRemoved(message);
}
TEST(GeneratedMessageReflectionTest, RemoveLastExtensions) {
unittest::TestAllExtensions message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllExtensions::descriptor());
TestUtil::SetAllExtensions(&message);
reflection_tester.RemoveLastRepeatedsViaReflection(&message);
TestUtil::ExpectLastRepeatedExtensionsRemoved(message);
}
TEST(GeneratedMessageReflectionTest, SwapRepeatedElements) {
unittest::TestAllTypes message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllTypes::descriptor());
TestUtil::SetAllFields(&message);
// Swap and test that fields are all swapped.
reflection_tester.SwapRepeatedsViaReflection(&message);
TestUtil::ExpectRepeatedsSwapped(message);
// Swap back and test that fields are all back to original values.
reflection_tester.SwapRepeatedsViaReflection(&message);
TestUtil::ExpectAllFieldsSet(message);
}
TEST(GeneratedMessageReflectionTest, SwapRepeatedElementsExtension) {
unittest::TestAllExtensions message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllExtensions::descriptor());
TestUtil::SetAllExtensions(&message);
// Swap and test that fields are all swapped.
reflection_tester.SwapRepeatedsViaReflection(&message);
TestUtil::ExpectRepeatedExtensionsSwapped(message);
// Swap back and test that fields are all back to original values.
reflection_tester.SwapRepeatedsViaReflection(&message);
TestUtil::ExpectAllExtensionsSet(message);
}
TEST(GeneratedMessageReflectionTest, Extensions) {
// Set every extension to a unique value then go back and check all those
// values.
unittest::TestAllExtensions message;
TestUtil::ReflectionTester reflection_tester(
unittest::TestAllExtensions::descriptor());
reflection_tester.SetAllFieldsViaReflection(&message);
TestUtil::ExpectAllExtensionsSet(message);
reflection_tester.ExpectAllFieldsSetViaReflection(message);
reflection_tester.ModifyRepeatedFieldsViaReflection(&message);
TestUtil::ExpectRepeatedExtensionsModified(message);
}
TEST(GeneratedMessageReflectionTest, FindExtensionTypeByNumber) {
const Reflection* reflection =
unittest::TestAllExtensions::default_instance().GetReflection();
const FieldDescriptor* extension1 =
unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName(
"optional_int32_extension");
const FieldDescriptor* extension2 =
unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName(
"repeated_string_extension");
EXPECT_EQ(extension1,
reflection->FindKnownExtensionByNumber(extension1->number()));
EXPECT_EQ(extension2,
reflection->FindKnownExtensionByNumber(extension2->number()));
// Non-existent extension.
EXPECT_TRUE(reflection->FindKnownExtensionByNumber(62341) == NULL);
// Extensions of TestAllExtensions should not show up as extensions of
// other types.
EXPECT_TRUE(unittest::TestAllTypes::default_instance().GetReflection()->
FindKnownExtensionByNumber(extension1->number()) == NULL);
}
TEST(GeneratedMessageReflectionTest, FindKnownExtensionByName) {
const Reflection* reflection =
unittest::TestAllExtensions::default_instance().GetReflection();
const FieldDescriptor* extension1 =
unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName(
"optional_int32_extension");
const FieldDescriptor* extension2 =
unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName(
"repeated_string_extension");
EXPECT_EQ(extension1,
reflection->FindKnownExtensionByName(extension1->full_name()));
EXPECT_EQ(extension2,
reflection->FindKnownExtensionByName(extension2->full_name()));
// Non-existent extension.
EXPECT_TRUE(reflection->FindKnownExtensionByName("no_such_ext") == NULL);
// Extensions of TestAllExtensions should not show up as extensions of
// other types.
EXPECT_TRUE(unittest::TestAllTypes::default_instance().GetReflection()->
FindKnownExtensionByName(extension1->full_name()) == NULL);
}
#ifdef GTEST_HAS_DEATH_TEST
TEST(GeneratedMessageReflectionTest, UsageErrors) {
unittest::TestAllTypes message;
const Reflection* reflection = message.GetReflection();
const Descriptor* descriptor = message.GetDescriptor();
#define f(NAME) descriptor->FindFieldByName(NAME)
// Testing every single failure mode would be too much work. Let's just
// check a few.
EXPECT_DEATH(
reflection->GetInt32(
message, descriptor->FindFieldByName("optional_int64")),
"Protocol Buffer reflection usage error:\n"
" Method : google::protobuf::Reflection::GetInt32\n"
" Message type: protobuf_unittest\\.TestAllTypes\n"
" Field : protobuf_unittest\\.TestAllTypes\\.optional_int64\n"
" Problem : Field is not the right type for this message:\n"
" Expected : CPPTYPE_INT32\n"
" Field type: CPPTYPE_INT64");
EXPECT_DEATH(
reflection->GetInt32(
message, descriptor->FindFieldByName("repeated_int32")),
"Protocol Buffer reflection usage error:\n"
" Method : google::protobuf::Reflection::GetInt32\n"
" Message type: protobuf_unittest.TestAllTypes\n"
" Field : protobuf_unittest.TestAllTypes.repeated_int32\n"
" Problem : Field is repeated; the method requires a singular field.");
EXPECT_DEATH(
reflection->GetInt32(
message, unittest::ForeignMessage::descriptor()->FindFieldByName("c")),
"Protocol Buffer reflection usage error:\n"
" Method : google::protobuf::Reflection::GetInt32\n"
" Message type: protobuf_unittest.TestAllTypes\n"
" Field : protobuf_unittest.ForeignMessage.c\n"
" Problem : Field does not match message type.");
EXPECT_DEATH(
reflection->HasField(
message, unittest::ForeignMessage::descriptor()->FindFieldByName("c")),
"Protocol Buffer reflection usage error:\n"
" Method : google::protobuf::Reflection::HasField\n"
" Message type: protobuf_unittest.TestAllTypes\n"
" Field : protobuf_unittest.ForeignMessage.c\n"
" Problem : Field does not match message type.");
#undef f
}
#endif // GTEST_HAS_DEATH_TEST
} // namespace
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,53 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/generated_message_util.h>
#include <limits>
namespace google {
namespace protobuf {
namespace internal {
double Infinity() {
return std::numeric_limits<double>::infinity();
}
double NaN() {
return std::numeric_limits<double>::quiet_NaN();
}
} // namespace internal
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,77 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This file contains miscellaneous helper code used by generated code --
// including lite types -- but which should not be used directly by users.
#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_UTIL_H__
#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_UTIL_H__
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
namespace io {
class CodedInputStream; // coded_stream.h
}
}
namespace protobuf {
namespace internal {
// Annotation for the compiler to emit a deprecation message if a field marked
// with option 'deprecated=true' is used in the code, or for other things in
// generated code which are deprecated.
//
// For internal use in the pb.cc files, deprecation warnings are suppressed
// there.
#undef DEPRECATED_PROTOBUF_FIELD
#if !defined(INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION)
# define PROTOBUF_DEPRECATED GOOGLE_ATTRIBUTE_DEPRECATED
#else
# define PROTOBUF_DEPRECATED
#endif
// Constants for special floating point values.
LIBPROTOBUF_EXPORT double Infinity();
LIBPROTOBUF_EXPORT double NaN();
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_GENERATED_MESSAGE_UTIL_H__

View file

@ -0,0 +1,830 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This implementation is heavily optimized to make reads and writes
// of small values (especially varints) as fast as possible. In
// particular, we optimize for the common case that a read or a write
// will not cross the end of the buffer, since we can avoid a lot
// of branching in this case.
#include <google/protobuf/io/coded_stream_inl.h>
#include <algorithm>
#include <limits.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
namespace protobuf {
namespace io {
namespace {
static const int kMaxVarintBytes = 10;
static const int kMaxVarint32Bytes = 5;
} // namespace
// CodedInputStream ==================================================
void CodedInputStream::BackUpInputToCurrentPosition() {
int backup_bytes = BufferSize() + buffer_size_after_limit_ + overflow_bytes_;
if (backup_bytes > 0) {
input_->BackUp(backup_bytes);
// total_bytes_read_ doesn't include overflow_bytes_.
total_bytes_read_ -= BufferSize() + buffer_size_after_limit_;
buffer_end_ = buffer_;
buffer_size_after_limit_ = 0;
overflow_bytes_ = 0;
}
}
inline void CodedInputStream::RecomputeBufferLimits() {
buffer_end_ += buffer_size_after_limit_;
int closest_limit = min(current_limit_, total_bytes_limit_);
if (closest_limit < total_bytes_read_) {
// The limit position is in the current buffer. We must adjust
// the buffer size accordingly.
buffer_size_after_limit_ = total_bytes_read_ - closest_limit;
buffer_end_ -= buffer_size_after_limit_;
} else {
buffer_size_after_limit_ = 0;
}
}
CodedInputStream::Limit CodedInputStream::PushLimit(int byte_limit) {
// Current position relative to the beginning of the stream.
int current_position = total_bytes_read_ -
(BufferSize() + buffer_size_after_limit_);
Limit old_limit = current_limit_;
// security: byte_limit is possibly evil, so check for negative values
// and overflow.
if (byte_limit >= 0 &&
byte_limit <= INT_MAX - current_position) {
current_limit_ = current_position + byte_limit;
} else {
// Negative or overflow.
current_limit_ = INT_MAX;
}
// We need to enforce all limits, not just the new one, so if the previous
// limit was before the new requested limit, we continue to enforce the
// previous limit.
current_limit_ = min(current_limit_, old_limit);
RecomputeBufferLimits();
return old_limit;
}
void CodedInputStream::PopLimit(Limit limit) {
// The limit passed in is actually the *old* limit, which we returned from
// PushLimit().
current_limit_ = limit;
RecomputeBufferLimits();
// We may no longer be at a legitimate message end. ReadTag() needs to be
// called again to find out.
legitimate_message_end_ = false;
}
int CodedInputStream::BytesUntilLimit() {
if (current_limit_ == INT_MAX) return -1;
int current_position = total_bytes_read_ -
(BufferSize() + buffer_size_after_limit_);
return current_limit_ - current_position;
}
void CodedInputStream::SetTotalBytesLimit(
int total_bytes_limit, int warning_threshold) {
// Make sure the limit isn't already past, since this could confuse other
// code.
int current_position = total_bytes_read_ -
(BufferSize() + buffer_size_after_limit_);
total_bytes_limit_ = max(current_position, total_bytes_limit);
total_bytes_warning_threshold_ = warning_threshold;
RecomputeBufferLimits();
}
void CodedInputStream::PrintTotalBytesLimitError() {
GOOGLE_LOG(ERROR) << "A protocol message was rejected because it was too "
"big (more than " << total_bytes_limit_
<< " bytes). To increase the limit (or to disable these "
"warnings), see CodedInputStream::SetTotalBytesLimit() "
"in google/protobuf/io/coded_stream.h.";
}
bool CodedInputStream::Skip(int count) {
if (count < 0) return false; // security: count is often user-supplied
const int original_buffer_size = BufferSize();
if (count <= original_buffer_size) {
// Just skipping within the current buffer. Easy.
Advance(count);
return true;
}
if (buffer_size_after_limit_ > 0) {
// We hit a limit inside this buffer. Advance to the limit and fail.
Advance(original_buffer_size);
return false;
}
count -= original_buffer_size;
buffer_ = NULL;
buffer_end_ = buffer_;
// Make sure this skip doesn't try to skip past the current limit.
int closest_limit = min(current_limit_, total_bytes_limit_);
int bytes_until_limit = closest_limit - total_bytes_read_;
if (bytes_until_limit < count) {
// We hit the limit. Skip up to it then fail.
if (bytes_until_limit > 0) {
total_bytes_read_ = closest_limit;
input_->Skip(bytes_until_limit);
}
return false;
}
total_bytes_read_ += count;
return input_->Skip(count);
}
bool CodedInputStream::GetDirectBufferPointer(const void** data, int* size) {
if (BufferSize() == 0 && !Refresh()) return false;
*data = buffer_;
*size = BufferSize();
return true;
}
bool CodedInputStream::ReadRaw(void* buffer, int size) {
int current_buffer_size;
while ((current_buffer_size = BufferSize()) < size) {
// Reading past end of buffer. Copy what we have, then refresh.
memcpy(buffer, buffer_, current_buffer_size);
buffer = reinterpret_cast<uint8*>(buffer) + current_buffer_size;
size -= current_buffer_size;
Advance(current_buffer_size);
if (!Refresh()) return false;
}
memcpy(buffer, buffer_, size);
Advance(size);
return true;
}
bool CodedInputStream::ReadString(string* buffer, int size) {
if (size < 0) return false; // security: size is often user-supplied
return InternalReadStringInline(buffer, size);
}
bool CodedInputStream::ReadStringFallback(string* buffer, int size) {
if (!buffer->empty()) {
buffer->clear();
}
int current_buffer_size;
while ((current_buffer_size = BufferSize()) < size) {
// Some STL implementations "helpfully" crash on buffer->append(NULL, 0).
if (current_buffer_size != 0) {
// Note: string1.append(string2) is O(string2.size()) (as opposed to
// O(string1.size() + string2.size()), which would be bad).
buffer->append(reinterpret_cast<const char*>(buffer_),
current_buffer_size);
}
size -= current_buffer_size;
Advance(current_buffer_size);
if (!Refresh()) return false;
}
buffer->append(reinterpret_cast<const char*>(buffer_), size);
Advance(size);
return true;
}
bool CodedInputStream::ReadLittleEndian32Fallback(uint32* value) {
uint8 bytes[sizeof(*value)];
const uint8* ptr;
if (BufferSize() >= sizeof(*value)) {
// Fast path: Enough bytes in the buffer to read directly.
ptr = buffer_;
Advance(sizeof(*value));
} else {
// Slow path: Had to read past the end of the buffer.
if (!ReadRaw(bytes, sizeof(*value))) return false;
ptr = bytes;
}
ReadLittleEndian32FromArray(ptr, value);
return true;
}
bool CodedInputStream::ReadLittleEndian64Fallback(uint64* value) {
uint8 bytes[sizeof(*value)];
const uint8* ptr;
if (BufferSize() >= sizeof(*value)) {
// Fast path: Enough bytes in the buffer to read directly.
ptr = buffer_;
Advance(sizeof(*value));
} else {
// Slow path: Had to read past the end of the buffer.
if (!ReadRaw(bytes, sizeof(*value))) return false;
ptr = bytes;
}
ReadLittleEndian64FromArray(ptr, value);
return true;
}
namespace {
inline const uint8* ReadVarint32FromArray(
const uint8* buffer, uint32* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE;
inline const uint8* ReadVarint32FromArray(const uint8* buffer, uint32* value) {
// Fast path: We have enough bytes left in the buffer to guarantee that
// this read won't cross the end, so we can skip the checks.
const uint8* ptr = buffer;
uint32 b;
uint32 result;
b = *(ptr++); result = (b & 0x7F) ; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done;
b = *(ptr++); result |= b << 28; if (!(b & 0x80)) goto done;
// If the input is larger than 32 bits, we still need to read it all
// and discard the high-order bits.
for (int i = 0; i < kMaxVarintBytes - kMaxVarint32Bytes; i++) {
b = *(ptr++); if (!(b & 0x80)) goto done;
}
// We have overrun the maximum size of a varint (10 bytes). Assume
// the data is corrupt.
return NULL;
done:
*value = result;
return ptr;
}
} // namespace
bool CodedInputStream::ReadVarint32Slow(uint32* value) {
uint64 result;
// Directly invoke ReadVarint64Fallback, since we already tried to optimize
// for one-byte varints.
if (!ReadVarint64Fallback(&result)) return false;
*value = (uint32)result;
return true;
}
bool CodedInputStream::ReadVarint32Fallback(uint32* value) {
if (BufferSize() >= kMaxVarintBytes ||
// Optimization: If the varint ends at exactly the end of the buffer,
// we can detect that and still use the fast path.
(buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) {
const uint8* end = ReadVarint32FromArray(buffer_, value);
if (end == NULL) return false;
buffer_ = end;
return true;
} else {
// Really slow case: we will incur the cost of an extra function call here,
// but moving this out of line reduces the size of this function, which
// improves the common case. In micro benchmarks, this is worth about 10-15%
return ReadVarint32Slow(value);
}
}
uint32 CodedInputStream::ReadTagSlow() {
if (buffer_ == buffer_end_) {
// Call refresh.
if (!Refresh()) {
// Refresh failed. Make sure that it failed due to EOF, not because
// we hit total_bytes_limit_, which, unlike normal limits, is not a
// valid place to end a message.
int current_position = total_bytes_read_ - buffer_size_after_limit_;
if (current_position >= total_bytes_limit_) {
// Hit total_bytes_limit_. But if we also hit the normal limit,
// we're still OK.
legitimate_message_end_ = current_limit_ == total_bytes_limit_;
} else {
legitimate_message_end_ = true;
}
return 0;
}
}
// For the slow path, just do a 64-bit read. Try to optimize for one-byte tags
// again, since we have now refreshed the buffer.
uint64 result;
if (!ReadVarint64(&result)) return 0;
return static_cast<uint32>(result);
}
uint32 CodedInputStream::ReadTagFallback() {
if (BufferSize() >= kMaxVarintBytes ||
// Optimization: If the varint ends at exactly the end of the buffer,
// we can detect that and still use the fast path.
(buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) {
uint32 tag;
const uint8* end = ReadVarint32FromArray(buffer_, &tag);
if (end == NULL) {
return 0;
}
buffer_ = end;
return tag;
} else {
// We are commonly at a limit when attempting to read tags. Try to quickly
// detect this case without making another function call.
if (buffer_ == buffer_end_ && buffer_size_after_limit_ > 0 &&
// Make sure that the limit we hit is not total_bytes_limit_, since
// in that case we still need to call Refresh() so that it prints an
// error.
total_bytes_read_ - buffer_size_after_limit_ < total_bytes_limit_) {
// We hit a byte limit.
legitimate_message_end_ = true;
return 0;
}
return ReadTagSlow();
}
}
bool CodedInputStream::ReadVarint64Slow(uint64* value) {
// Slow path: This read might cross the end of the buffer, so we
// need to check and refresh the buffer if and when it does.
uint64 result = 0;
int count = 0;
uint32 b;
do {
if (count == kMaxVarintBytes) return false;
while (buffer_ == buffer_end_) {
if (!Refresh()) return false;
}
b = *buffer_;
result |= static_cast<uint64>(b & 0x7F) << (7 * count);
Advance(1);
++count;
} while (b & 0x80);
*value = result;
return true;
}
bool CodedInputStream::ReadVarint64Fallback(uint64* value) {
if (BufferSize() >= kMaxVarintBytes ||
// Optimization: If the varint ends at exactly the end of the buffer,
// we can detect that and still use the fast path.
(buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) {
// Fast path: We have enough bytes left in the buffer to guarantee that
// this read won't cross the end, so we can skip the checks.
const uint8* ptr = buffer_;
uint32 b;
// Splitting into 32-bit pieces gives better performance on 32-bit
// processors.
uint32 part0 = 0, part1 = 0, part2 = 0;
b = *(ptr++); part0 = (b & 0x7F) ; if (!(b & 0x80)) goto done;
b = *(ptr++); part0 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done;
b = *(ptr++); part0 |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done;
b = *(ptr++); part0 |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done;
b = *(ptr++); part1 = (b & 0x7F) ; if (!(b & 0x80)) goto done;
b = *(ptr++); part1 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done;
b = *(ptr++); part1 |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done;
b = *(ptr++); part1 |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done;
b = *(ptr++); part2 = (b & 0x7F) ; if (!(b & 0x80)) goto done;
b = *(ptr++); part2 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done;
// We have overrun the maximum size of a varint (10 bytes). The data
// must be corrupt.
return NULL;
done:
Advance(ptr - buffer_);
*value = (static_cast<uint64>(part0) ) |
(static_cast<uint64>(part1) << 28) |
(static_cast<uint64>(part2) << 56);
return true;
} else {
return ReadVarint64Slow(value);
}
}
bool CodedInputStream::Refresh() {
GOOGLE_DCHECK_EQ(0, BufferSize());
if (buffer_size_after_limit_ > 0 || overflow_bytes_ > 0 ||
total_bytes_read_ == current_limit_) {
// We've hit a limit. Stop.
int current_position = total_bytes_read_ - buffer_size_after_limit_;
if (current_position >= total_bytes_limit_ &&
total_bytes_limit_ != current_limit_) {
// Hit total_bytes_limit_.
PrintTotalBytesLimitError();
}
return false;
}
if (total_bytes_warning_threshold_ >= 0 &&
total_bytes_read_ >= total_bytes_warning_threshold_) {
GOOGLE_LOG(WARNING) << "Reading dangerously large protocol message. If the "
"message turns out to be larger than "
<< total_bytes_limit_ << " bytes, parsing will be halted "
"for security reasons. To increase the limit (or to "
"disable these warnings), see "
"CodedInputStream::SetTotalBytesLimit() in "
"google/protobuf/io/coded_stream.h.";
// Don't warn again for this stream.
total_bytes_warning_threshold_ = -1;
}
const void* void_buffer;
int buffer_size;
if (input_->Next(&void_buffer, &buffer_size)) {
buffer_ = reinterpret_cast<const uint8*>(void_buffer);
buffer_end_ = buffer_ + buffer_size;
GOOGLE_CHECK_GE(buffer_size, 0);
if (total_bytes_read_ <= INT_MAX - buffer_size) {
total_bytes_read_ += buffer_size;
} else {
// Overflow. Reset buffer_end_ to not include the bytes beyond INT_MAX.
// We can't get that far anyway, because total_bytes_limit_ is guaranteed
// to be less than it. We need to keep track of the number of bytes
// we discarded, though, so that we can call input_->BackUp() to back
// up over them on destruction.
// The following line is equivalent to:
// overflow_bytes_ = total_bytes_read_ + buffer_size - INT_MAX;
// except that it avoids overflows. Signed integer overflow has
// undefined results according to the C standard.
overflow_bytes_ = total_bytes_read_ - (INT_MAX - buffer_size);
buffer_end_ -= overflow_bytes_;
total_bytes_read_ = INT_MAX;
}
RecomputeBufferLimits();
return true;
} else {
buffer_ = NULL;
buffer_end_ = NULL;
return false;
}
}
// CodedOutputStream =================================================
CodedOutputStream::CodedOutputStream(ZeroCopyOutputStream* output)
: output_(output),
buffer_(NULL),
buffer_size_(0),
total_bytes_(0),
had_error_(false) {
// Eagerly Refresh() so buffer space is immediately available.
Refresh();
// The Refresh() may have failed. If the client doesn't write any data,
// though, don't consider this an error. If the client does write data, then
// another Refresh() will be attempted and it will set the error once again.
had_error_ = false;
}
CodedOutputStream::~CodedOutputStream() {
if (buffer_size_ > 0) {
output_->BackUp(buffer_size_);
}
}
bool CodedOutputStream::Skip(int count) {
if (count < 0) return false;
while (count > buffer_size_) {
count -= buffer_size_;
if (!Refresh()) return false;
}
Advance(count);
return true;
}
bool CodedOutputStream::GetDirectBufferPointer(void** data, int* size) {
if (buffer_size_ == 0 && !Refresh()) return false;
*data = buffer_;
*size = buffer_size_;
return true;
}
void CodedOutputStream::WriteRaw(const void* data, int size) {
while (buffer_size_ < size) {
memcpy(buffer_, data, buffer_size_);
size -= buffer_size_;
data = reinterpret_cast<const uint8*>(data) + buffer_size_;
if (!Refresh()) return;
}
memcpy(buffer_, data, size);
Advance(size);
}
uint8* CodedOutputStream::WriteRawToArray(
const void* data, int size, uint8* target) {
memcpy(target, data, size);
return target + size;
}
void CodedOutputStream::WriteLittleEndian32(uint32 value) {
uint8 bytes[sizeof(value)];
bool use_fast = buffer_size_ >= sizeof(value);
uint8* ptr = use_fast ? buffer_ : bytes;
WriteLittleEndian32ToArray(value, ptr);
if (use_fast) {
Advance(sizeof(value));
} else {
WriteRaw(bytes, sizeof(value));
}
}
void CodedOutputStream::WriteLittleEndian64(uint64 value) {
uint8 bytes[sizeof(value)];
bool use_fast = buffer_size_ >= sizeof(value);
uint8* ptr = use_fast ? buffer_ : bytes;
WriteLittleEndian64ToArray(value, ptr);
if (use_fast) {
Advance(sizeof(value));
} else {
WriteRaw(bytes, sizeof(value));
}
}
inline uint8* CodedOutputStream::WriteVarint32FallbackToArrayInline(
uint32 value, uint8* target) {
target[0] = static_cast<uint8>(value | 0x80);
if (value >= (1 << 7)) {
target[1] = static_cast<uint8>((value >> 7) | 0x80);
if (value >= (1 << 14)) {
target[2] = static_cast<uint8>((value >> 14) | 0x80);
if (value >= (1 << 21)) {
target[3] = static_cast<uint8>((value >> 21) | 0x80);
if (value >= (1 << 28)) {
target[4] = static_cast<uint8>(value >> 28);
return target + 5;
} else {
target[3] &= 0x7F;
return target + 4;
}
} else {
target[2] &= 0x7F;
return target + 3;
}
} else {
target[1] &= 0x7F;
return target + 2;
}
} else {
target[0] &= 0x7F;
return target + 1;
}
}
void CodedOutputStream::WriteVarint32(uint32 value) {
if (buffer_size_ >= kMaxVarint32Bytes) {
// Fast path: We have enough bytes left in the buffer to guarantee that
// this write won't cross the end, so we can skip the checks.
uint8* target = buffer_;
uint8* end = WriteVarint32FallbackToArrayInline(value, target);
int size = end - target;
Advance(size);
} else {
// Slow path: This write might cross the end of the buffer, so we
// compose the bytes first then use WriteRaw().
uint8 bytes[kMaxVarint32Bytes];
int size = 0;
while (value > 0x7F) {
bytes[size++] = (static_cast<uint8>(value) & 0x7F) | 0x80;
value >>= 7;
}
bytes[size++] = static_cast<uint8>(value) & 0x7F;
WriteRaw(bytes, size);
}
}
uint8* CodedOutputStream::WriteVarint32FallbackToArray(
uint32 value, uint8* target) {
return WriteVarint32FallbackToArrayInline(value, target);
}
inline uint8* CodedOutputStream::WriteVarint64ToArrayInline(
uint64 value, uint8* target) {
// Splitting into 32-bit pieces gives better performance on 32-bit
// processors.
uint32 part0 = static_cast<uint32>(value );
uint32 part1 = static_cast<uint32>(value >> 28);
uint32 part2 = static_cast<uint32>(value >> 56);
int size;
// Here we can't really optimize for small numbers, since the value is
// split into three parts. Cheking for numbers < 128, for instance,
// would require three comparisons, since you'd have to make sure part1
// and part2 are zero. However, if the caller is using 64-bit integers,
// it is likely that they expect the numbers to often be very large, so
// we probably don't want to optimize for small numbers anyway. Thus,
// we end up with a hardcoded binary search tree...
if (part2 == 0) {
if (part1 == 0) {
if (part0 < (1 << 14)) {
if (part0 < (1 << 7)) {
size = 1; goto size1;
} else {
size = 2; goto size2;
}
} else {
if (part0 < (1 << 21)) {
size = 3; goto size3;
} else {
size = 4; goto size4;
}
}
} else {
if (part1 < (1 << 14)) {
if (part1 < (1 << 7)) {
size = 5; goto size5;
} else {
size = 6; goto size6;
}
} else {
if (part1 < (1 << 21)) {
size = 7; goto size7;
} else {
size = 8; goto size8;
}
}
}
} else {
if (part2 < (1 << 7)) {
size = 9; goto size9;
} else {
size = 10; goto size10;
}
}
GOOGLE_LOG(FATAL) << "Can't get here.";
size10: target[9] = static_cast<uint8>((part2 >> 7) | 0x80);
size9 : target[8] = static_cast<uint8>((part2 ) | 0x80);
size8 : target[7] = static_cast<uint8>((part1 >> 21) | 0x80);
size7 : target[6] = static_cast<uint8>((part1 >> 14) | 0x80);
size6 : target[5] = static_cast<uint8>((part1 >> 7) | 0x80);
size5 : target[4] = static_cast<uint8>((part1 ) | 0x80);
size4 : target[3] = static_cast<uint8>((part0 >> 21) | 0x80);
size3 : target[2] = static_cast<uint8>((part0 >> 14) | 0x80);
size2 : target[1] = static_cast<uint8>((part0 >> 7) | 0x80);
size1 : target[0] = static_cast<uint8>((part0 ) | 0x80);
target[size-1] &= 0x7F;
return target + size;
}
void CodedOutputStream::WriteVarint64(uint64 value) {
if (buffer_size_ >= kMaxVarintBytes) {
// Fast path: We have enough bytes left in the buffer to guarantee that
// this write won't cross the end, so we can skip the checks.
uint8* target = buffer_;
uint8* end = WriteVarint64ToArrayInline(value, target);
int size = end - target;
Advance(size);
} else {
// Slow path: This write might cross the end of the buffer, so we
// compose the bytes first then use WriteRaw().
uint8 bytes[kMaxVarintBytes];
int size = 0;
while (value > 0x7F) {
bytes[size++] = (static_cast<uint8>(value) & 0x7F) | 0x80;
value >>= 7;
}
bytes[size++] = static_cast<uint8>(value) & 0x7F;
WriteRaw(bytes, size);
}
}
uint8* CodedOutputStream::WriteVarint64ToArray(
uint64 value, uint8* target) {
return WriteVarint64ToArrayInline(value, target);
}
bool CodedOutputStream::Refresh() {
void* void_buffer;
if (output_->Next(&void_buffer, &buffer_size_)) {
buffer_ = reinterpret_cast<uint8*>(void_buffer);
total_bytes_ += buffer_size_;
return true;
} else {
buffer_ = NULL;
buffer_size_ = 0;
had_error_ = true;
return false;
}
}
int CodedOutputStream::VarintSize32Fallback(uint32 value) {
if (value < (1 << 7)) {
return 1;
} else if (value < (1 << 14)) {
return 2;
} else if (value < (1 << 21)) {
return 3;
} else if (value < (1 << 28)) {
return 4;
} else {
return 5;
}
}
int CodedOutputStream::VarintSize64(uint64 value) {
if (value < (1ull << 35)) {
if (value < (1ull << 7)) {
return 1;
} else if (value < (1ull << 14)) {
return 2;
} else if (value < (1ull << 21)) {
return 3;
} else if (value < (1ull << 28)) {
return 4;
} else {
return 5;
}
} else {
if (value < (1ull << 42)) {
return 6;
} else if (value < (1ull << 49)) {
return 7;
} else if (value < (1ull << 56)) {
return 8;
} else if (value < (1ull << 63)) {
return 9;
} else {
return 10;
}
}
}
} // namespace io
} // namespace protobuf
} // namespace google

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,64 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: jasonh@google.com (Jason Hsueh)
//
// Implements methods of coded_stream.h that need to be inlined for performance
// reasons, but should not be defined in a public header.
#ifndef GOOGLE_PROTOBUF_IO_CODED_STREAM_INL_H__
#define GOOGLE_PROTOBUF_IO_CODED_STREAM_INL_H__
#include <google/protobuf/io/coded_stream.h>
#include <string>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
namespace protobuf {
namespace io {
inline bool CodedInputStream::InternalReadStringInline(string* buffer,
int size) {
if (size < 0) return false; // security: size is often user-supplied
if (BufferSize() >= size) {
STLStringResizeUninitialized(buffer, size);
memcpy(string_as_array(buffer), buffer_, size);
Advance(size);
return true;
}
return ReadStringFallback(buffer, size);
}
} // namespace io
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_IO_CODED_STREAM_INL_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,320 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: brianolson@google.com (Brian Olson)
//
// This file contains the implementation of classes GzipInputStream and
// GzipOutputStream.
#include "config.h"
#if HAVE_ZLIB
#include <google/protobuf/io/gzip_stream.h>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
namespace io {
static const int kDefaultBufferSize = 65536;
GzipInputStream::GzipInputStream(
ZeroCopyInputStream* sub_stream, Format format, int buffer_size)
: format_(format), sub_stream_(sub_stream), zerror_(Z_OK) {
zcontext_.zalloc = Z_NULL;
zcontext_.zfree = Z_NULL;
zcontext_.opaque = Z_NULL;
zcontext_.total_out = 0;
zcontext_.next_in = NULL;
zcontext_.avail_in = 0;
zcontext_.total_in = 0;
zcontext_.msg = NULL;
if (buffer_size == -1) {
output_buffer_length_ = kDefaultBufferSize;
} else {
output_buffer_length_ = buffer_size;
}
output_buffer_ = operator new(output_buffer_length_);
GOOGLE_CHECK(output_buffer_ != NULL);
zcontext_.next_out = static_cast<Bytef*>(output_buffer_);
zcontext_.avail_out = output_buffer_length_;
output_position_ = output_buffer_;
}
GzipInputStream::~GzipInputStream() {
operator delete(output_buffer_);
zerror_ = inflateEnd(&zcontext_);
}
int GzipInputStream::Inflate(int flush) {
if ((zerror_ == Z_OK) && (zcontext_.avail_out == 0)) {
// previous inflate filled output buffer. don't change input params yet.
} else if (zcontext_.avail_in == 0) {
const void* in;
int in_size;
bool first = zcontext_.next_in == NULL;
bool ok = sub_stream_->Next(&in, &in_size);
if (!ok) {
zcontext_.next_out = NULL;
zcontext_.avail_out = 0;
return Z_STREAM_END;
}
zcontext_.next_in = static_cast<Bytef*>(const_cast<void*>(in));
zcontext_.avail_in = in_size;
if (first) {
int windowBitsFormat = 0;
switch (format_) {
case GZIP: windowBitsFormat = 16; break;
case AUTO: windowBitsFormat = 32; break;
case ZLIB: windowBitsFormat = 0; break;
}
int error = inflateInit2(&zcontext_,
/* windowBits */15 | windowBitsFormat);
if (error != Z_OK) {
return error;
}
}
}
zcontext_.next_out = static_cast<Bytef*>(output_buffer_);
zcontext_.avail_out = output_buffer_length_;
output_position_ = output_buffer_;
int error = inflate(&zcontext_, flush);
return error;
}
void GzipInputStream::DoNextOutput(const void** data, int* size) {
*data = output_position_;
*size = ((uintptr_t)zcontext_.next_out) - ((uintptr_t)output_position_);
output_position_ = zcontext_.next_out;
}
// implements ZeroCopyInputStream ----------------------------------
bool GzipInputStream::Next(const void** data, int* size) {
bool ok = (zerror_ == Z_OK) || (zerror_ == Z_STREAM_END)
|| (zerror_ == Z_BUF_ERROR);
if ((!ok) || (zcontext_.next_out == NULL)) {
return false;
}
if (zcontext_.next_out != output_position_) {
DoNextOutput(data, size);
return true;
}
if (zerror_ == Z_STREAM_END) {
*data = NULL;
*size = 0;
return false;
}
zerror_ = Inflate(Z_NO_FLUSH);
if ((zerror_ == Z_STREAM_END) && (zcontext_.next_out == NULL)) {
// The underlying stream's Next returned false inside Inflate.
return false;
}
ok = (zerror_ == Z_OK) || (zerror_ == Z_STREAM_END)
|| (zerror_ == Z_BUF_ERROR);
if (!ok) {
return false;
}
DoNextOutput(data, size);
return true;
}
void GzipInputStream::BackUp(int count) {
output_position_ = reinterpret_cast<void*>(
reinterpret_cast<uintptr_t>(output_position_) - count);
}
bool GzipInputStream::Skip(int count) {
const void* data;
int size;
bool ok = Next(&data, &size);
while (ok && (size < count)) {
count -= size;
ok = Next(&data, &size);
}
if (size > count) {
BackUp(size - count);
}
return ok;
}
int64 GzipInputStream::ByteCount() const {
return zcontext_.total_out +
(((uintptr_t)zcontext_.next_out) - ((uintptr_t)output_position_));
}
// =========================================================================
GzipOutputStream::Options::Options()
: format(GZIP),
buffer_size(kDefaultBufferSize),
compression_level(Z_DEFAULT_COMPRESSION),
compression_strategy(Z_DEFAULT_STRATEGY) {}
GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream) {
Init(sub_stream, Options());
}
GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* sub_stream,
const Options& options) {
Init(sub_stream, options);
}
GzipOutputStream::GzipOutputStream(
ZeroCopyOutputStream* sub_stream, Format format, int buffer_size) {
Options options;
options.format = format;
if (buffer_size != -1) {
options.buffer_size = buffer_size;
}
Init(sub_stream, options);
}
void GzipOutputStream::Init(ZeroCopyOutputStream* sub_stream,
const Options& options) {
sub_stream_ = sub_stream;
sub_data_ = NULL;
sub_data_size_ = 0;
input_buffer_length_ = options.buffer_size;
input_buffer_ = operator new(input_buffer_length_);
GOOGLE_CHECK(input_buffer_ != NULL);
zcontext_.zalloc = Z_NULL;
zcontext_.zfree = Z_NULL;
zcontext_.opaque = Z_NULL;
zcontext_.next_out = NULL;
zcontext_.avail_out = 0;
zcontext_.total_out = 0;
zcontext_.next_in = NULL;
zcontext_.avail_in = 0;
zcontext_.total_in = 0;
zcontext_.msg = NULL;
// default to GZIP format
int windowBitsFormat = 16;
if (options.format == ZLIB) {
windowBitsFormat = 0;
}
zerror_ = deflateInit2(
&zcontext_,
options.compression_level,
Z_DEFLATED,
/* windowBits */15 | windowBitsFormat,
/* memLevel (default) */8,
options.compression_strategy);
}
GzipOutputStream::~GzipOutputStream() {
Close();
if (input_buffer_ != NULL) {
operator delete(input_buffer_);
}
}
// private
int GzipOutputStream::Deflate(int flush) {
int error = Z_OK;
do {
if ((sub_data_ == NULL) || (zcontext_.avail_out == 0)) {
bool ok = sub_stream_->Next(&sub_data_, &sub_data_size_);
if (!ok) {
sub_data_ = NULL;
sub_data_size_ = 0;
return Z_BUF_ERROR;
}
GOOGLE_CHECK_GT(sub_data_size_, 0);
zcontext_.next_out = static_cast<Bytef*>(sub_data_);
zcontext_.avail_out = sub_data_size_;
}
error = deflate(&zcontext_, flush);
} while (error == Z_OK && zcontext_.avail_out == 0);
if (((flush == Z_FULL_FLUSH) || (flush == Z_FINISH))
&& (zcontext_.avail_out != sub_data_size_)) {
// Notify lower layer of data.
sub_stream_->BackUp(zcontext_.avail_out);
// We don't own the buffer anymore.
sub_data_ = NULL;
sub_data_size_ = 0;
}
return error;
}
// implements ZeroCopyOutputStream ---------------------------------
bool GzipOutputStream::Next(void** data, int* size) {
if ((zerror_ != Z_OK) && (zerror_ != Z_BUF_ERROR)) {
return false;
}
if (zcontext_.avail_in != 0) {
zerror_ = Deflate(Z_NO_FLUSH);
if (zerror_ != Z_OK) {
return false;
}
}
if (zcontext_.avail_in == 0) {
// all input was consumed. reset the buffer.
zcontext_.next_in = static_cast<Bytef*>(input_buffer_);
zcontext_.avail_in = input_buffer_length_;
*data = input_buffer_;
*size = input_buffer_length_;
} else {
// The loop in Deflate should consume all avail_in
GOOGLE_LOG(DFATAL) << "Deflate left bytes unconsumed";
}
return true;
}
void GzipOutputStream::BackUp(int count) {
GOOGLE_CHECK_GE(zcontext_.avail_in, count);
zcontext_.avail_in -= count;
}
int64 GzipOutputStream::ByteCount() const {
return zcontext_.total_in + zcontext_.avail_in;
}
bool GzipOutputStream::Flush() {
do {
zerror_ = Deflate(Z_FULL_FLUSH);
} while (zerror_ == Z_OK);
return zerror_ == Z_OK;
}
bool GzipOutputStream::Close() {
if ((zerror_ != Z_OK) && (zerror_ != Z_BUF_ERROR)) {
return false;
}
do {
zerror_ = Deflate(Z_FINISH);
} while (zerror_ == Z_OK);
zerror_ = deflateEnd(&zcontext_);
bool ok = zerror_ == Z_OK;
zerror_ = Z_STREAM_END;
return ok;
}
} // namespace io
} // namespace protobuf
} // namespace google
#endif // HAVE_ZLIB

View file

@ -0,0 +1,207 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: brianolson@google.com (Brian Olson)
//
// This file contains the definition for classes GzipInputStream and
// GzipOutputStream.
//
// GzipInputStream decompresses data from an underlying
// ZeroCopyInputStream and provides the decompressed data as a
// ZeroCopyInputStream.
//
// GzipOutputStream is an ZeroCopyOutputStream that compresses data to
// an underlying ZeroCopyOutputStream.
#ifndef GOOGLE_PROTOBUF_IO_GZIP_STREAM_H__
#define GOOGLE_PROTOBUF_IO_GZIP_STREAM_H__
#include <zlib.h>
#include <google/protobuf/io/zero_copy_stream.h>
namespace google {
namespace protobuf {
namespace io {
// A ZeroCopyInputStream that reads compressed data through zlib
class LIBPROTOBUF_EXPORT GzipInputStream : public ZeroCopyInputStream {
public:
// Format key for constructor
enum Format {
// zlib will autodetect gzip header or deflate stream
AUTO = 0,
// GZIP streams have some extra header data for file attributes.
GZIP = 1,
// Simpler zlib stream format.
ZLIB = 2,
};
// buffer_size and format may be -1 for default of 64kB and GZIP format
explicit GzipInputStream(
ZeroCopyInputStream* sub_stream,
Format format = AUTO,
int buffer_size = -1);
virtual ~GzipInputStream();
// Return last error message or NULL if no error.
inline const char* ZlibErrorMessage() const {
return zcontext_.msg;
}
inline int ZlibErrorCode() const {
return zerror_;
}
// implements ZeroCopyInputStream ----------------------------------
bool Next(const void** data, int* size);
void BackUp(int count);
bool Skip(int count);
int64 ByteCount() const;
private:
Format format_;
ZeroCopyInputStream* sub_stream_;
z_stream zcontext_;
int zerror_;
void* output_buffer_;
void* output_position_;
size_t output_buffer_length_;
int Inflate(int flush);
void DoNextOutput(const void** data, int* size);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GzipInputStream);
};
class LIBPROTOBUF_EXPORT GzipOutputStream : public ZeroCopyOutputStream {
public:
// Format key for constructor
enum Format {
// GZIP streams have some extra header data for file attributes.
GZIP = 1,
// Simpler zlib stream format.
ZLIB = 2,
};
struct Options {
// Defaults to GZIP.
Format format;
// What size buffer to use internally. Defaults to 64kB.
int buffer_size;
// A number between 0 and 9, where 0 is no compression and 9 is best
// compression. Defaults to Z_DEFAULT_COMPRESSION (see zlib.h).
int compression_level;
// Defaults to Z_DEFAULT_STRATEGY. Can also be set to Z_FILTERED,
// Z_HUFFMAN_ONLY, or Z_RLE. See the documentation for deflateInit2 in
// zlib.h for definitions of these constants.
int compression_strategy;
Options(); // Initializes with default values.
};
// Create a GzipOutputStream with default options.
explicit GzipOutputStream(ZeroCopyOutputStream* sub_stream);
// Create a GzipOutputStream with the given options.
GzipOutputStream(
ZeroCopyOutputStream* sub_stream,
const Options& options);
// DEPRECATED: Use one of the above constructors instead.
GzipOutputStream(
ZeroCopyOutputStream* sub_stream,
Format format,
int buffer_size = -1) GOOGLE_ATTRIBUTE_DEPRECATED;
virtual ~GzipOutputStream();
// Return last error message or NULL if no error.
inline const char* ZlibErrorMessage() const {
return zcontext_.msg;
}
inline int ZlibErrorCode() const {
return zerror_;
}
// Flushes data written so far to zipped data in the underlying stream.
// It is the caller's responsibility to flush the underlying stream if
// necessary.
// Compression may be less efficient stopping and starting around flushes.
// Returns true if no error.
bool Flush();
// Writes out all data and closes the gzip stream.
// It is the caller's responsibility to close the underlying stream if
// necessary.
// Returns true if no error.
bool Close();
// implements ZeroCopyOutputStream ---------------------------------
bool Next(void** data, int* size);
void BackUp(int count);
int64 ByteCount() const;
private:
ZeroCopyOutputStream* sub_stream_;
// Result from calling Next() on sub_stream_
void* sub_data_;
int sub_data_size_;
z_stream zcontext_;
int zerror_;
void* input_buffer_;
size_t input_buffer_length_;
// Shared constructor code.
void Init(ZeroCopyOutputStream* sub_stream, const Options& options);
// Do some compression.
// Takes zlib flush mode.
// Returns zlib error code.
int Deflate(int flush);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GzipOutputStream);
};
} // namespace io
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_IO_GZIP_STREAM_H__

View file

@ -0,0 +1,44 @@
#!/bin/sh -x
#
# Protocol Buffers - Google's data interchange format
# Copyright 2009 Google Inc. All rights reserved.
# http://code.google.com/p/protobuf/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Author: brianolson@google.com (Brian Olson)
#
# Test compatibility between command line gzip/gunzip binaries and
# ZeroCopyStream versions.
TESTFILE=Makefile
(./zcgzip < ${TESTFILE} | gunzip | cmp - ${TESTFILE}) && \
(gzip < ${TESTFILE} | ./zcgunzip | cmp - ${TESTFILE})
# Result of "(cmd) && (cmd)" implicitly becomes result of this script
# and thus the test.

View file

@ -0,0 +1,54 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This file exists solely to document the google::protobuf::io namespace.
// It is not compiled into anything, but it may be read by an automated
// documentation generator.
namespace google {
namespace protobuf {
// Auxiliary classes used for I/O.
//
// The Protocol Buffer library uses the classes in this package to deal with
// I/O and encoding/decoding raw bytes. Most users will not need to
// deal with this package. However, users who want to adapt the system to
// work with their own I/O abstractions -- e.g., to allow Protocol Buffers
// to be read from a different kind of input stream without the need for a
// temporary buffer -- should take a closer look.
namespace io {}
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,188 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace io {
Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter)
: variable_delimiter_(variable_delimiter),
output_(output),
buffer_(NULL),
buffer_size_(0),
at_start_of_line_(true),
failed_(false) {
}
Printer::~Printer() {
// Only BackUp() if we're sure we've successfully called Next() at least once.
if (buffer_size_ > 0) {
output_->BackUp(buffer_size_);
}
}
void Printer::Print(const map<string, string>& variables, const char* text) {
int size = strlen(text);
int pos = 0; // The number of bytes we've written so far.
for (int i = 0; i < size; i++) {
if (text[i] == '\n') {
// Saw newline. If there is more text, we may need to insert an indent
// here. So, write what we have so far, including the '\n'.
WriteRaw(text + pos, i - pos + 1);
pos = i + 1;
// Setting this true will cause the next WriteRaw() to insert an indent
// first.
at_start_of_line_ = true;
} else if (text[i] == variable_delimiter_) {
// Saw the start of a variable name.
// Write what we have so far.
WriteRaw(text + pos, i - pos);
pos = i + 1;
// Find closing delimiter.
const char* end = strchr(text + pos, variable_delimiter_);
if (end == NULL) {
GOOGLE_LOG(DFATAL) << " Unclosed variable name.";
end = text + pos;
}
int endpos = end - text;
string varname(text + pos, endpos - pos);
if (varname.empty()) {
// Two delimiters in a row reduce to a literal delimiter character.
WriteRaw(&variable_delimiter_, 1);
} else {
// Replace with the variable's value.
map<string, string>::const_iterator iter = variables.find(varname);
if (iter == variables.end()) {
GOOGLE_LOG(DFATAL) << " Undefined variable: " << varname;
} else {
WriteRaw(iter->second.data(), iter->second.size());
}
}
// Advance past this variable.
i = endpos;
pos = endpos + 1;
}
}
// Write the rest.
WriteRaw(text + pos, size - pos);
}
void Printer::Print(const char* text) {
static map<string, string> empty;
Print(empty, text);
}
void Printer::Print(const char* text,
const char* variable, const string& value) {
map<string, string> vars;
vars[variable] = value;
Print(vars, text);
}
void Printer::Print(const char* text,
const char* variable1, const string& value1,
const char* variable2, const string& value2) {
map<string, string> vars;
vars[variable1] = value1;
vars[variable2] = value2;
Print(vars, text);
}
void Printer::Indent() {
indent_ += " ";
}
void Printer::Outdent() {
if (indent_.empty()) {
GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent().";
return;
}
indent_.resize(indent_.size() - 2);
}
void Printer::PrintRaw(const string& data) {
WriteRaw(data.data(), data.size());
}
void Printer::PrintRaw(const char* data) {
if (failed_) return;
WriteRaw(data, strlen(data));
}
void Printer::WriteRaw(const char* data, int size) {
if (failed_) return;
if (size == 0) return;
if (at_start_of_line_) {
// Insert an indent.
at_start_of_line_ = false;
WriteRaw(indent_.data(), indent_.size());
if (failed_) return;
}
while (size > buffer_size_) {
// Data exceeds space in the buffer. Copy what we can and request a
// new buffer.
memcpy(buffer_, data, buffer_size_);
data += buffer_size_;
size -= buffer_size_;
void* void_buffer;
failed_ = !output_->Next(&void_buffer, &buffer_size_);
if (failed_) return;
buffer_ = reinterpret_cast<char*>(void_buffer);
}
// Buffer is big enough to receive the data; copy it.
memcpy(buffer_, data, size);
buffer_ += size;
buffer_size_ -= size;
}
} // namespace io
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,132 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Utility class for writing text to a ZeroCopyOutputStream.
#ifndef GOOGLE_PROTOBUF_IO_PRINTER_H__
#define GOOGLE_PROTOBUF_IO_PRINTER_H__
#include <string>
#include <map>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
namespace io {
class ZeroCopyOutputStream; // zero_copy_stream.h
// This simple utility class assists in code generation. It basically
// allows the caller to define a set of variables and then output some
// text with variable substitutions. Example usage:
//
// Printer printer(output, '$');
// map<string, string> vars;
// vars["name"] = "Bob";
// printer.Print(vars, "My name is $name$.");
//
// The above writes "My name is Bob." to the output stream.
//
// Printer aggressively enforces correct usage, crashing (with assert failures)
// in the case of undefined variables in debug builds. This helps greatly in
// debugging code which uses it.
class LIBPROTOBUF_EXPORT Printer {
public:
// Create a printer that writes text to the given output stream. Use the
// given character as the delimiter for variables.
Printer(ZeroCopyOutputStream* output, char variable_delimiter);
~Printer();
// Print some text after applying variable substitutions. If a particular
// variable in the text is not defined, this will crash. Variables to be
// substituted are identified by their names surrounded by delimiter
// characters (as given to the constructor). The variable bindings are
// defined by the given map.
void Print(const map<string, string>& variables, const char* text);
// Like the first Print(), except the substitutions are given as parameters.
void Print(const char* text);
// Like the first Print(), except the substitutions are given as parameters.
void Print(const char* text, const char* variable, const string& value);
// Like the first Print(), except the substitutions are given as parameters.
void Print(const char* text, const char* variable1, const string& value1,
const char* variable2, const string& value2);
// TODO(kenton): Overloaded versions with more variables? Two seems
// to be enough.
// Indent text by two spaces. After calling Indent(), two spaces will be
// inserted at the beginning of each line of text. Indent() may be called
// multiple times to produce deeper indents.
void Indent();
// Reduces the current indent level by two spaces, or crashes if the indent
// level is zero.
void Outdent();
// Write a string to the output buffer.
// This method does not look for newlines to add indentation.
void PrintRaw(const string& data);
// Write a zero-delimited string to output buffer.
// This method does not look for newlines to add indentation.
void PrintRaw(const char* data);
// Write some bytes to the output buffer.
// This method does not look for newlines to add indentation.
void WriteRaw(const char* data, int size);
// True if any write to the underlying stream failed. (We don't just
// crash in this case because this is an I/O failure, not a programming
// error.)
bool failed() const { return failed_; }
private:
const char variable_delimiter_;
ZeroCopyOutputStream* const output_;
char* buffer_;
int buffer_size_;
string indent_;
bool at_start_of_line_;
bool failed_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Printer);
};
} // namespace io
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_IO_PRINTER_H__

View file

@ -0,0 +1,261 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <vector>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace io {
namespace {
// Each test repeats over several block sizes in order to test both cases
// where particular writes cross a buffer boundary and cases where they do
// not.
TEST(Printer, EmptyPrinter) {
char buffer[8192];
const int block_size = 100;
ArrayOutputStream output(buffer, GOOGLE_ARRAYSIZE(buffer), block_size);
Printer printer(&output, '\0');
EXPECT_TRUE(!printer.failed());
}
TEST(Printer, BasicPrinting) {
char buffer[8192];
for (int block_size = 1; block_size < 512; block_size *= 2) {
ArrayOutputStream output(buffer, sizeof(buffer), block_size);
{
Printer printer(&output, '\0');
printer.Print("Hello World!");
printer.Print(" This is the same line.\n");
printer.Print("But this is a new one.\nAnd this is another one.");
EXPECT_FALSE(printer.failed());
}
buffer[output.ByteCount()] = '\0';
EXPECT_STREQ("Hello World! This is the same line.\n"
"But this is a new one.\n"
"And this is another one.",
buffer);
}
}
TEST(Printer, WriteRaw) {
char buffer[8192];
for (int block_size = 1; block_size < 512; block_size *= 2) {
ArrayOutputStream output(buffer, sizeof(buffer), block_size);
{
string string_obj = "From an object\n";
Printer printer(&output, '$');
printer.WriteRaw("Hello World!", 12);
printer.PrintRaw(" This is the same line.\n");
printer.PrintRaw("But this is a new one.\nAnd this is another one.");
printer.WriteRaw("\n", 1);
printer.PrintRaw(string_obj);
EXPECT_FALSE(printer.failed());
}
buffer[output.ByteCount()] = '\0';
EXPECT_STREQ("Hello World! This is the same line.\n"
"But this is a new one.\n"
"And this is another one."
"\n"
"From an object\n",
buffer);
}
}
TEST(Printer, VariableSubstitution) {
char buffer[8192];
for (int block_size = 1; block_size < 512; block_size *= 2) {
ArrayOutputStream output(buffer, sizeof(buffer), block_size);
{
Printer printer(&output, '$');
map<string, string> vars;
vars["foo"] = "World";
vars["bar"] = "$foo$";
vars["abcdefg"] = "1234";
printer.Print(vars, "Hello $foo$!\nbar = $bar$\n");
printer.PrintRaw("RawBit\n");
printer.Print(vars, "$abcdefg$\nA literal dollar sign: $$");
vars["foo"] = "blah";
printer.Print(vars, "\nNow foo = $foo$.");
EXPECT_FALSE(printer.failed());
}
buffer[output.ByteCount()] = '\0';
EXPECT_STREQ("Hello World!\n"
"bar = $foo$\n"
"RawBit\n"
"1234\n"
"A literal dollar sign: $\n"
"Now foo = blah.",
buffer);
}
}
TEST(Printer, InlineVariableSubstitution) {
char buffer[8192];
ArrayOutputStream output(buffer, sizeof(buffer));
{
Printer printer(&output, '$');
printer.Print("Hello $foo$!\n", "foo", "World");
printer.PrintRaw("RawBit\n");
printer.Print("$foo$ $bar$\n", "foo", "one", "bar", "two");
EXPECT_FALSE(printer.failed());
}
buffer[output.ByteCount()] = '\0';
EXPECT_STREQ("Hello World!\n"
"RawBit\n"
"one two\n",
buffer);
}
TEST(Printer, Indenting) {
char buffer[8192];
for (int block_size = 1; block_size < 512; block_size *= 2) {
ArrayOutputStream output(buffer, sizeof(buffer), block_size);
{
Printer printer(&output, '$');
map<string, string> vars;
vars["newline"] = "\n";
printer.Print("This is not indented.\n");
printer.Indent();
printer.Print("This is indented\nAnd so is this\n");
printer.Outdent();
printer.Print("But this is not.");
printer.Indent();
printer.Print(" And this is still the same line.\n"
"But this is indented.\n");
printer.PrintRaw("RawBit has indent at start\n");
printer.PrintRaw("but not after a raw newline\n");
printer.Print(vars, "Note that a newline in a variable will break "
"indenting, as we see$newline$here.\n");
printer.Indent();
printer.Print("And this");
printer.Outdent();
printer.Outdent();
printer.Print(" is double-indented\nBack to normal.");
EXPECT_FALSE(printer.failed());
}
buffer[output.ByteCount()] = '\0';
EXPECT_STREQ(
"This is not indented.\n"
" This is indented\n"
" And so is this\n"
"But this is not. And this is still the same line.\n"
" But this is indented.\n"
" RawBit has indent at start\n"
"but not after a raw newline\n"
"Note that a newline in a variable will break indenting, as we see\n"
"here.\n"
" And this is double-indented\n"
"Back to normal.",
buffer);
}
}
// Death tests do not work on Windows as of yet.
#ifdef GTEST_HAS_DEATH_TEST
TEST(Printer, Death) {
char buffer[8192];
ArrayOutputStream output(buffer, sizeof(buffer));
Printer printer(&output, '$');
EXPECT_DEBUG_DEATH(printer.Print("$nosuchvar$"), "Undefined variable");
EXPECT_DEBUG_DEATH(printer.Print("$unclosed"), "Unclosed variable name");
EXPECT_DEBUG_DEATH(printer.Outdent(), "without matching Indent");
}
#endif // GTEST_HAS_DEATH_TEST
TEST(Printer, WriteFailure) {
char buffer[16];
ArrayOutputStream output(buffer, sizeof(buffer));
Printer printer(&output, '$');
// Print 16 bytes to fill the buffer exactly (should not fail).
printer.Print("0123456789abcdef");
EXPECT_FALSE(printer.failed());
// Try to print one more byte (should fail).
printer.Print(" ");
EXPECT_TRUE(printer.failed());
// Should not crash
printer.Print("blah");
EXPECT_TRUE(printer.failed());
// Buffer should contain the first 16 bytes written.
EXPECT_EQ("0123456789abcdef", string(buffer, sizeof(buffer)));
}
} // namespace
} // namespace io
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,691 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Here we have a hand-written lexer. At first you might ask yourself,
// "Hand-written text processing? Is Kenton crazy?!" Well, first of all,
// yes I am crazy, but that's beside the point. There are actually reasons
// why I ended up writing this this way.
//
// The traditional approach to lexing is to use lex to generate a lexer for
// you. Unfortunately, lex's output is ridiculously ugly and difficult to
// integrate cleanly with C++ code, especially abstract code or code meant
// as a library. Better parser-generators exist but would add dependencies
// which most users won't already have, which we'd like to avoid. (GNU flex
// has a C++ output option, but it's still ridiculously ugly, non-abstract,
// and not library-friendly.)
//
// The next approach that any good software engineer should look at is to
// use regular expressions. And, indeed, I did. I have code which
// implements this same class using regular expressions. It's about 200
// lines shorter. However:
// - Rather than error messages telling you "This string has an invalid
// escape sequence at line 5, column 45", you get error messages like
// "Parse error on line 5". Giving more precise errors requires adding
// a lot of code that ends up basically as complex as the hand-coded
// version anyway.
// - The regular expression to match a string literal looks like this:
// kString = new RE("(\"([^\"\\\\]|" // non-escaped
// "\\\\[abfnrtv?\"'\\\\0-7]|" // normal escape
// "\\\\x[0-9a-fA-F])*\"|" // hex escape
// "\'([^\'\\\\]|" // Also support single-quotes.
// "\\\\[abfnrtv?\"'\\\\0-7]|"
// "\\\\x[0-9a-fA-F])*\')");
// Verifying the correctness of this line noise is actually harder than
// verifying the correctness of ConsumeString(), defined below. I'm not
// even confident that the above is correct, after staring at it for some
// time.
// - PCRE is fast, but there's still more overhead involved than the code
// below.
// - Sadly, regular expressions are not part of the C standard library, so
// using them would require depending on some other library. For the
// open source release, this could be really annoying. Nobody likes
// downloading one piece of software just to find that they need to
// download something else to make it work, and in all likelihood
// people downloading Protocol Buffers will already be doing so just
// to make something else work. We could include a copy of PCRE with
// our code, but that obligates us to keep it up-to-date and just seems
// like a big waste just to save 200 lines of code.
//
// On a similar but unrelated note, I'm even scared to use ctype.h.
// Apparently functions like isalpha() are locale-dependent. So, if we used
// that, then if this code is being called from some program that doesn't
// have its locale set to "C", it would behave strangely. We can't just set
// the locale to "C" ourselves since we might break the calling program that
// way, particularly if it is multi-threaded. WTF? Someone please let me
// (Kenton) know if I'm missing something here...
//
// I'd love to hear about other alternatives, though, as this code isn't
// exactly pretty.
#include <google/protobuf/io/tokenizer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace io {
namespace {
// As mentioned above, I don't trust ctype.h due to the presence of "locales".
// So, I have written replacement functions here. Someone please smack me if
// this is a bad idea or if there is some way around this.
//
// These "character classes" are designed to be used in template methods.
// For instance, Tokenizer::ConsumeZeroOrMore<Whitespace>() will eat
// whitespace.
// Note: No class is allowed to contain '\0', since this is used to mark end-
// of-input and is handled specially.
#define CHARACTER_CLASS(NAME, EXPRESSION) \
class NAME { \
public: \
static inline bool InClass(char c) { \
return EXPRESSION; \
} \
}
CHARACTER_CLASS(Whitespace, c == ' ' || c == '\n' || c == '\t' ||
c == '\r' || c == '\v' || c == '\f');
CHARACTER_CLASS(Unprintable, c < ' ' && c > '\0');
CHARACTER_CLASS(Digit, '0' <= c && c <= '9');
CHARACTER_CLASS(OctalDigit, '0' <= c && c <= '7');
CHARACTER_CLASS(HexDigit, ('0' <= c && c <= '9') ||
('a' <= c && c <= 'f') ||
('A' <= c && c <= 'F'));
CHARACTER_CLASS(Letter, ('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z') ||
(c == '_'));
CHARACTER_CLASS(Alphanumeric, ('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z') ||
('0' <= c && c <= '9') ||
(c == '_'));
CHARACTER_CLASS(Escape, c == 'a' || c == 'b' || c == 'f' || c == 'n' ||
c == 'r' || c == 't' || c == 'v' || c == '\\' ||
c == '?' || c == '\'' || c == '\"');
#undef CHARACTER_CLASS
// Given a char, interpret it as a numeric digit and return its value.
// This supports any number base up to 36.
inline int DigitValue(char digit) {
if ('0' <= digit && digit <= '9') return digit - '0';
if ('a' <= digit && digit <= 'z') return digit - 'a' + 10;
if ('A' <= digit && digit <= 'Z') return digit - 'A' + 10;
return -1;
}
// Inline because it's only used in one place.
inline char TranslateEscape(char c) {
switch (c) {
case 'a': return '\a';
case 'b': return '\b';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'v': return '\v';
case '\\': return '\\';
case '?': return '\?'; // Trigraphs = :(
case '\'': return '\'';
case '"': return '\"';
// We expect escape sequences to have been validated separately.
default: return '?';
}
}
} // anonymous namespace
ErrorCollector::~ErrorCollector() {}
// ===================================================================
Tokenizer::Tokenizer(ZeroCopyInputStream* input,
ErrorCollector* error_collector)
: input_(input),
error_collector_(error_collector),
buffer_(NULL),
buffer_size_(0),
buffer_pos_(0),
read_error_(false),
line_(0),
column_(0),
token_start_(-1),
allow_f_after_float_(false),
comment_style_(CPP_COMMENT_STYLE) {
current_.line = 0;
current_.column = 0;
current_.type = TYPE_START;
Refresh();
}
Tokenizer::~Tokenizer() {
// If we had any buffer left unread, return it to the underlying stream
// so that someone else can read it.
if (buffer_size_ > buffer_pos_) {
input_->BackUp(buffer_size_ - buffer_pos_);
}
}
// -------------------------------------------------------------------
// Internal helpers.
void Tokenizer::NextChar() {
// Update our line and column counters based on the character being
// consumed.
if (current_char_ == '\n') {
++line_;
column_ = 0;
} else if (current_char_ == '\t') {
column_ += kTabWidth - column_ % kTabWidth;
} else {
++column_;
}
// Advance to the next character.
++buffer_pos_;
if (buffer_pos_ < buffer_size_) {
current_char_ = buffer_[buffer_pos_];
} else {
Refresh();
}
}
void Tokenizer::Refresh() {
if (read_error_) {
current_char_ = '\0';
return;
}
// If we're in a token, append the rest of the buffer to it.
if (token_start_ >= 0 && token_start_ < buffer_size_) {
current_.text.append(buffer_ + token_start_, buffer_size_ - token_start_);
token_start_ = 0;
}
const void* data = NULL;
buffer_ = NULL;
buffer_pos_ = 0;
do {
if (!input_->Next(&data, &buffer_size_)) {
// end of stream (or read error)
buffer_size_ = 0;
read_error_ = true;
current_char_ = '\0';
return;
}
} while (buffer_size_ == 0);
buffer_ = static_cast<const char*>(data);
current_char_ = buffer_[0];
}
inline void Tokenizer::StartToken() {
token_start_ = buffer_pos_;
current_.type = TYPE_START; // Just for the sake of initializing it.
current_.text.clear();
current_.line = line_;
current_.column = column_;
}
inline void Tokenizer::EndToken() {
// Note: The if() is necessary because some STL implementations crash when
// you call string::append(NULL, 0), presumably because they are trying to
// be helpful by detecting the NULL pointer, even though there's nothing
// wrong with reading zero bytes from NULL.
if (buffer_pos_ != token_start_) {
current_.text.append(buffer_ + token_start_, buffer_pos_ - token_start_);
}
token_start_ = -1;
}
// -------------------------------------------------------------------
// Helper methods that consume characters.
template<typename CharacterClass>
inline bool Tokenizer::LookingAt() {
return CharacterClass::InClass(current_char_);
}
template<typename CharacterClass>
inline bool Tokenizer::TryConsumeOne() {
if (CharacterClass::InClass(current_char_)) {
NextChar();
return true;
} else {
return false;
}
}
inline bool Tokenizer::TryConsume(char c) {
if (current_char_ == c) {
NextChar();
return true;
} else {
return false;
}
}
template<typename CharacterClass>
inline void Tokenizer::ConsumeZeroOrMore() {
while (CharacterClass::InClass(current_char_)) {
NextChar();
}
}
template<typename CharacterClass>
inline void Tokenizer::ConsumeOneOrMore(const char* error) {
if (!CharacterClass::InClass(current_char_)) {
AddError(error);
} else {
do {
NextChar();
} while (CharacterClass::InClass(current_char_));
}
}
// -------------------------------------------------------------------
// Methods that read whole patterns matching certain kinds of tokens
// or comments.
void Tokenizer::ConsumeString(char delimiter) {
while (true) {
switch (current_char_) {
case '\0':
case '\n': {
AddError("String literals cannot cross line boundaries.");
return;
}
case '\\': {
// An escape sequence.
NextChar();
if (TryConsumeOne<Escape>()) {
// Valid escape sequence.
} else if (TryConsumeOne<OctalDigit>()) {
// Possibly followed by two more octal digits, but these will
// just be consumed by the main loop anyway so we don't need
// to do so explicitly here.
} else if (TryConsume('x') || TryConsume('X')) {
if (!TryConsumeOne<HexDigit>()) {
AddError("Expected hex digits for escape sequence.");
}
// Possibly followed by another hex digit, but again we don't care.
} else {
AddError("Invalid escape sequence in string literal.");
}
break;
}
default: {
if (current_char_ == delimiter) {
NextChar();
return;
}
NextChar();
break;
}
}
}
}
Tokenizer::TokenType Tokenizer::ConsumeNumber(bool started_with_zero,
bool started_with_dot) {
bool is_float = false;
if (started_with_zero && (TryConsume('x') || TryConsume('X'))) {
// A hex number (started with "0x").
ConsumeOneOrMore<HexDigit>("\"0x\" must be followed by hex digits.");
} else if (started_with_zero && LookingAt<Digit>()) {
// An octal number (had a leading zero).
ConsumeZeroOrMore<OctalDigit>();
if (LookingAt<Digit>()) {
AddError("Numbers starting with leading zero must be in octal.");
ConsumeZeroOrMore<Digit>();
}
} else {
// A decimal number.
if (started_with_dot) {
is_float = true;
ConsumeZeroOrMore<Digit>();
} else {
ConsumeZeroOrMore<Digit>();
if (TryConsume('.')) {
is_float = true;
ConsumeZeroOrMore<Digit>();
}
}
if (TryConsume('e') || TryConsume('E')) {
is_float = true;
TryConsume('-') || TryConsume('+');
ConsumeOneOrMore<Digit>("\"e\" must be followed by exponent.");
}
if (allow_f_after_float_ && (TryConsume('f') || TryConsume('F'))) {
is_float = true;
}
}
if (LookingAt<Letter>()) {
AddError("Need space between number and identifier.");
} else if (current_char_ == '.') {
if (is_float) {
AddError(
"Already saw decimal point or exponent; can't have another one.");
} else {
AddError("Hex and octal numbers must be integers.");
}
}
return is_float ? TYPE_FLOAT : TYPE_INTEGER;
}
void Tokenizer::ConsumeLineComment() {
while (current_char_ != '\0' && current_char_ != '\n') {
NextChar();
}
TryConsume('\n');
}
void Tokenizer::ConsumeBlockComment() {
int start_line = line_;
int start_column = column_ - 2;
while (true) {
while (current_char_ != '\0' &&
current_char_ != '*' &&
current_char_ != '/') {
NextChar();
}
if (TryConsume('*') && TryConsume('/')) {
// End of comment.
break;
} else if (TryConsume('/') && current_char_ == '*') {
// Note: We didn't consume the '*' because if there is a '/' after it
// we want to interpret that as the end of the comment.
AddError(
"\"/*\" inside block comment. Block comments cannot be nested.");
} else if (current_char_ == '\0') {
AddError("End-of-file inside block comment.");
error_collector_->AddError(
start_line, start_column, " Comment started here.");
break;
}
}
}
// -------------------------------------------------------------------
bool Tokenizer::Next() {
TokenType last_token_type = current_.type;
// Did we skip any characters after the last token?
bool skipped_stuff = false;
while (!read_error_) {
if (TryConsumeOne<Whitespace>()) {
ConsumeZeroOrMore<Whitespace>();
} else if (comment_style_ == CPP_COMMENT_STYLE && TryConsume('/')) {
// Starting a comment?
if (TryConsume('/')) {
ConsumeLineComment();
} else if (TryConsume('*')) {
ConsumeBlockComment();
} else {
// Oops, it was just a slash. Return it.
current_.type = TYPE_SYMBOL;
current_.text = "/";
current_.line = line_;
current_.column = column_ - 1;
return true;
}
} else if (comment_style_ == SH_COMMENT_STYLE && TryConsume('#')) {
ConsumeLineComment();
} else if (LookingAt<Unprintable>() || current_char_ == '\0') {
AddError("Invalid control characters encountered in text.");
NextChar();
// Skip more unprintable characters, too. But, remember that '\0' is
// also what current_char_ is set to after EOF / read error. We have
// to be careful not to go into an infinite loop of trying to consume
// it, so make sure to check read_error_ explicitly before consuming
// '\0'.
while (TryConsumeOne<Unprintable>() ||
(!read_error_ && TryConsume('\0'))) {
// Ignore.
}
} else {
// Reading some sort of token.
StartToken();
if (TryConsumeOne<Letter>()) {
ConsumeZeroOrMore<Alphanumeric>();
current_.type = TYPE_IDENTIFIER;
} else if (TryConsume('0')) {
current_.type = ConsumeNumber(true, false);
} else if (TryConsume('.')) {
// This could be the beginning of a floating-point number, or it could
// just be a '.' symbol.
if (TryConsumeOne<Digit>()) {
// It's a floating-point number.
if (last_token_type == TYPE_IDENTIFIER && !skipped_stuff) {
// We don't accept syntax like "blah.123".
error_collector_->AddError(line_, column_ - 2,
"Need space between identifier and decimal point.");
}
current_.type = ConsumeNumber(false, true);
} else {
current_.type = TYPE_SYMBOL;
}
} else if (TryConsumeOne<Digit>()) {
current_.type = ConsumeNumber(false, false);
} else if (TryConsume('\"')) {
ConsumeString('\"');
current_.type = TYPE_STRING;
} else if (TryConsume('\'')) {
ConsumeString('\'');
current_.type = TYPE_STRING;
} else {
NextChar();
current_.type = TYPE_SYMBOL;
}
EndToken();
return true;
}
skipped_stuff = true;
}
// EOF
current_.type = TYPE_END;
current_.text.clear();
current_.line = line_;
current_.column = column_;
return false;
}
// -------------------------------------------------------------------
// Token-parsing helpers. Remember that these don't need to report
// errors since any errors should already have been reported while
// tokenizing. Also, these can assume that whatever text they
// are given is text that the tokenizer actually parsed as a token
// of the given type.
bool Tokenizer::ParseInteger(const string& text, uint64 max_value,
uint64* output) {
// Sadly, we can't just use strtoul() since it is only 32-bit and strtoull()
// is non-standard. I hate the C standard library. :(
// return strtoull(text.c_str(), NULL, 0);
const char* ptr = text.c_str();
int base = 10;
if (ptr[0] == '0') {
if (ptr[1] == 'x' || ptr[1] == 'X') {
// This is hex.
base = 16;
ptr += 2;
} else {
// This is octal.
base = 8;
}
}
uint64 result = 0;
for (; *ptr != '\0'; ptr++) {
int digit = DigitValue(*ptr);
GOOGLE_LOG_IF(DFATAL, digit < 0 || digit >= base)
<< " Tokenizer::ParseInteger() passed text that could not have been"
" tokenized as an integer: " << CEscape(text);
if (digit > max_value || result > (max_value - digit) / base) {
// Overflow.
return false;
}
result = result * base + digit;
}
*output = result;
return true;
}
double Tokenizer::ParseFloat(const string& text) {
const char* start = text.c_str();
char* end;
double result = NoLocaleStrtod(start, &end);
// "1e" is not a valid float, but if the tokenizer reads it, it will
// report an error but still return it as a valid token. We need to
// accept anything the tokenizer could possibly return, error or not.
if (*end == 'e' || *end == 'E') {
++end;
if (*end == '-' || *end == '+') ++end;
}
// If the Tokenizer had allow_f_after_float_ enabled, the float may be
// suffixed with the letter 'f'.
if (*end == 'f' || *end == 'F') {
++end;
}
GOOGLE_LOG_IF(DFATAL, end - start != text.size() || *start == '-')
<< " Tokenizer::ParseFloat() passed text that could not have been"
" tokenized as a float: " << CEscape(text);
return result;
}
void Tokenizer::ParseStringAppend(const string& text, string* output) {
// Reminder: text[0] is always the quote character. (If text is
// empty, it's invalid, so we'll just return.)
if (text.empty()) {
GOOGLE_LOG(DFATAL)
<< " Tokenizer::ParseStringAppend() passed text that could not"
" have been tokenized as a string: " << CEscape(text);
return;
}
output->reserve(output->size() + text.size());
// Loop through the string copying characters to "output" and
// interpreting escape sequences. Note that any invalid escape
// sequences or other errors were already reported while tokenizing.
// In this case we do not need to produce valid results.
for (const char* ptr = text.c_str() + 1; *ptr != '\0'; ptr++) {
if (*ptr == '\\' && ptr[1] != '\0') {
// An escape sequence.
++ptr;
if (OctalDigit::InClass(*ptr)) {
// An octal escape. May one, two, or three digits.
int code = DigitValue(*ptr);
if (OctalDigit::InClass(ptr[1])) {
++ptr;
code = code * 8 + DigitValue(*ptr);
}
if (OctalDigit::InClass(ptr[1])) {
++ptr;
code = code * 8 + DigitValue(*ptr);
}
output->push_back(static_cast<char>(code));
} else if (*ptr == 'x') {
// A hex escape. May zero, one, or two digits. (The zero case
// will have been caught as an error earlier.)
int code = 0;
if (HexDigit::InClass(ptr[1])) {
++ptr;
code = DigitValue(*ptr);
}
if (HexDigit::InClass(ptr[1])) {
++ptr;
code = code * 16 + DigitValue(*ptr);
}
output->push_back(static_cast<char>(code));
} else {
// Some other escape code.
output->push_back(TranslateEscape(*ptr));
}
} else if (*ptr == text[0]) {
// Ignore quote matching the starting quote.
} else {
output->push_back(*ptr);
}
}
return;
}
} // namespace io
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,303 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Class for parsing tokenized text from a ZeroCopyInputStream.
#ifndef GOOGLE_PROTOBUF_IO_TOKENIZER_H__
#define GOOGLE_PROTOBUF_IO_TOKENIZER_H__
#include <string>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
namespace io {
class ZeroCopyInputStream; // zero_copy_stream.h
// Defined in this file.
class ErrorCollector;
class Tokenizer;
// Abstract interface for an object which collects the errors that occur
// during parsing. A typical implementation might simply print the errors
// to stdout.
class LIBPROTOBUF_EXPORT ErrorCollector {
public:
inline ErrorCollector() {}
virtual ~ErrorCollector();
// Indicates that there was an error in the input at the given line and
// column numbers. The numbers are zero-based, so you may want to add
// 1 to each before printing them.
virtual void AddError(int line, int column, const string& message) = 0;
// Indicates that there was a warning in the input at the given line and
// column numbers. The numbers are zero-based, so you may want to add
// 1 to each before printing them.
virtual void AddWarning(int line, int column, const string& message) { }
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector);
};
// This class converts a stream of raw text into a stream of tokens for
// the protocol definition parser to parse. The tokens recognized are
// similar to those that make up the C language; see the TokenType enum for
// precise descriptions. Whitespace and comments are skipped. By default,
// C- and C++-style comments are recognized, but other styles can be used by
// calling set_comment_style().
class LIBPROTOBUF_EXPORT Tokenizer {
public:
// Construct a Tokenizer that reads and tokenizes text from the given
// input stream and writes errors to the given error_collector.
// The caller keeps ownership of input and error_collector.
Tokenizer(ZeroCopyInputStream* input, ErrorCollector* error_collector);
~Tokenizer();
enum TokenType {
TYPE_START, // Next() has not yet been called.
TYPE_END, // End of input reached. "text" is empty.
TYPE_IDENTIFIER, // A sequence of letters, digits, and underscores, not
// starting with a digit. It is an error for a number
// to be followed by an identifier with no space in
// between.
TYPE_INTEGER, // A sequence of digits representing an integer. Normally
// the digits are decimal, but a prefix of "0x" indicates
// a hex number and a leading zero indicates octal, just
// like with C numeric literals. A leading negative sign
// is NOT included in the token; it's up to the parser to
// interpret the unary minus operator on its own.
TYPE_FLOAT, // A floating point literal, with a fractional part and/or
// an exponent. Always in decimal. Again, never
// negative.
TYPE_STRING, // A quoted sequence of escaped characters. Either single
// or double quotes can be used, but they must match.
// A string literal cannot cross a line break.
TYPE_SYMBOL, // Any other printable character, like '!' or '+'.
// Symbols are always a single character, so "!+$%" is
// four tokens.
};
// Structure representing a token read from the token stream.
struct Token {
TokenType type;
string text; // The exact text of the token as it appeared in
// the input. e.g. tokens of TYPE_STRING will still
// be escaped and in quotes.
// "line" and "column" specify the position of the first character of
// the token within the input stream. They are zero-based.
int line;
int column;
};
// Get the current token. This is updated when Next() is called. Before
// the first call to Next(), current() has type TYPE_START and no contents.
const Token& current();
// Advance to the next token. Returns false if the end of the input is
// reached.
bool Next();
// Parse helpers ---------------------------------------------------
// Parses a TYPE_FLOAT token. This never fails, so long as the text actually
// comes from a TYPE_FLOAT token parsed by Tokenizer. If it doesn't, the
// result is undefined (possibly an assert failure).
static double ParseFloat(const string& text);
// Parses a TYPE_STRING token. This never fails, so long as the text actually
// comes from a TYPE_STRING token parsed by Tokenizer. If it doesn't, the
// result is undefined (possibly an assert failure).
static void ParseString(const string& text, string* output);
// Identical to ParseString, but appends to output.
static void ParseStringAppend(const string& text, string* output);
// Parses a TYPE_INTEGER token. Returns false if the result would be
// greater than max_value. Otherwise, returns true and sets *output to the
// result. If the text is not from a Token of type TYPE_INTEGER originally
// parsed by a Tokenizer, the result is undefined (possibly an assert
// failure).
static bool ParseInteger(const string& text, uint64 max_value,
uint64* output);
// Options ---------------------------------------------------------
// Set true to allow floats to be suffixed with the letter 'f'. Tokens
// which would otherwise be integers but which have the 'f' suffix will be
// forced to be interpreted as floats. For all other purposes, the 'f' is
// ignored.
void set_allow_f_after_float(bool value) { allow_f_after_float_ = value; }
// Valid values for set_comment_style().
enum CommentStyle {
// Line comments begin with "//", block comments are delimited by "/*" and
// "*/".
CPP_COMMENT_STYLE,
// Line comments begin with "#". No way to write block comments.
SH_COMMENT_STYLE
};
// Sets the comment style.
void set_comment_style(CommentStyle style) { comment_style_ = style; }
// -----------------------------------------------------------------
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Tokenizer);
Token current_; // Returned by current().
ZeroCopyInputStream* input_;
ErrorCollector* error_collector_;
char current_char_; // == buffer_[buffer_pos_], updated by NextChar().
const char* buffer_; // Current buffer returned from input_.
int buffer_size_; // Size of buffer_.
int buffer_pos_; // Current position within the buffer.
bool read_error_; // Did we previously encounter a read error?
// Line and column number of current_char_ within the whole input stream.
int line_;
int column_;
// Position in buffer_ where StartToken() was called. If the token
// started in the previous buffer, this is zero, and current_.text already
// contains the part of the token from the previous buffer. If not
// currently parsing a token, this is -1.
int token_start_;
// Options.
bool allow_f_after_float_;
CommentStyle comment_style_;
// Since we count columns we need to interpret tabs somehow. We'll take
// the standard 8-character definition for lack of any way to do better.
static const int kTabWidth = 8;
// -----------------------------------------------------------------
// Helper methods.
// Consume this character and advance to the next one.
void NextChar();
// Read a new buffer from the input.
void Refresh();
// Called when the current character is the first character of a new
// token (not including whitespace or comments).
inline void StartToken();
// Called when the current character is the first character after the
// end of the last token. After this returns, current_.text will
// contain all text consumed since StartToken() was called.
inline void EndToken();
// Convenience method to add an error at the current line and column.
void AddError(const string& message) {
error_collector_->AddError(line_, column_, message);
}
// -----------------------------------------------------------------
// The following four methods are used to consume tokens of specific
// types. They are actually used to consume all characters *after*
// the first, since the calling function consumes the first character
// in order to decide what kind of token is being read.
// Read and consume a string, ending when the given delimiter is
// consumed.
void ConsumeString(char delimiter);
// Read and consume a number, returning TYPE_FLOAT or TYPE_INTEGER
// depending on what was read. This needs to know if the first
// character was a zero in order to correctly recognize hex and octal
// numbers.
// It also needs to know if the first characted was a . to parse floating
// point correctly.
TokenType ConsumeNumber(bool started_with_zero, bool started_with_dot);
// Consume the rest of a line.
void ConsumeLineComment();
// Consume until "*/".
void ConsumeBlockComment();
// -----------------------------------------------------------------
// These helper methods make the parsing code more readable. The
// "character classes" refered to are defined at the top of the .cc file.
// Basically it is a C++ class with one method:
// static bool InClass(char c);
// The method returns true if c is a member of this "class", like "Letter"
// or "Digit".
// Returns true if the current character is of the given character
// class, but does not consume anything.
template<typename CharacterClass>
inline bool LookingAt();
// If the current character is in the given class, consume it and return
// true. Otherwise return false.
// e.g. TryConsumeOne<Letter>()
template<typename CharacterClass>
inline bool TryConsumeOne();
// Like above, but try to consume the specific character indicated.
inline bool TryConsume(char c);
// Consume zero or more of the given character class.
template<typename CharacterClass>
inline void ConsumeZeroOrMore();
// Consume one or more of the given character class or log the given
// error message.
// e.g. ConsumeOneOrMore<Digit>("Expected digits.");
template<typename CharacterClass>
inline void ConsumeOneOrMore(const char* error);
};
// inline methods ====================================================
inline const Tokenizer::Token& Tokenizer::current() {
return current_;
}
inline void Tokenizer::ParseString(const string& text, string* output) {
output->clear();
ParseStringAppend(text, output);
}
} // namespace io
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_IO_TOKENIZER_H__

View file

@ -0,0 +1,743 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <vector>
#include <math.h>
#include <limits.h>
#include <google/protobuf/io/tokenizer.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace io {
namespace {
// ===================================================================
// Data-Driven Test Infrastructure
// TODO(kenton): This is copied from coded_stream_unittest. This is
// temporary until these fetaures are integrated into gTest itself.
// TEST_1D and TEST_2D are macros I'd eventually like to see added to
// gTest. These macros can be used to declare tests which should be
// run multiple times, once for each item in some input array. TEST_1D
// tests all cases in a single input array. TEST_2D tests all
// combinations of cases from two arrays. The arrays must be statically
// defined such that the GOOGLE_ARRAYSIZE() macro works on them. Example:
//
// int kCases[] = {1, 2, 3, 4}
// TEST_1D(MyFixture, MyTest, kCases) {
// EXPECT_GT(kCases_case, 0);
// }
//
// This test iterates through the numbers 1, 2, 3, and 4 and tests that
// they are all grater than zero. In case of failure, the exact case
// which failed will be printed. The case type must be printable using
// ostream::operator<<.
#define TEST_1D(FIXTURE, NAME, CASES) \
class FIXTURE##_##NAME##_DD : public FIXTURE { \
protected: \
template <typename CaseType> \
void DoSingleCase(const CaseType& CASES##_case); \
}; \
\
TEST_F(FIXTURE##_##NAME##_DD, NAME) { \
for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES); i++) { \
SCOPED_TRACE(testing::Message() \
<< #CASES " case #" << i << ": " << CASES[i]); \
DoSingleCase(CASES[i]); \
} \
} \
\
template <typename CaseType> \
void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType& CASES##_case)
#define TEST_2D(FIXTURE, NAME, CASES1, CASES2) \
class FIXTURE##_##NAME##_DD : public FIXTURE { \
protected: \
template <typename CaseType1, typename CaseType2> \
void DoSingleCase(const CaseType1& CASES1##_case, \
const CaseType2& CASES2##_case); \
}; \
\
TEST_F(FIXTURE##_##NAME##_DD, NAME) { \
for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES1); i++) { \
for (int j = 0; j < GOOGLE_ARRAYSIZE(CASES2); j++) { \
SCOPED_TRACE(testing::Message() \
<< #CASES1 " case #" << i << ": " << CASES1[i] << ", " \
<< #CASES2 " case #" << j << ": " << CASES2[j]); \
DoSingleCase(CASES1[i], CASES2[j]); \
} \
} \
} \
\
template <typename CaseType1, typename CaseType2> \
void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType1& CASES1##_case, \
const CaseType2& CASES2##_case)
// -------------------------------------------------------------------
// An input stream that is basically like an ArrayInputStream but sometimes
// returns empty buffers, just to throw us off.
class TestInputStream : public ZeroCopyInputStream {
public:
TestInputStream(const void* data, int size, int block_size)
: array_stream_(data, size, block_size), counter_(0) {}
~TestInputStream() {}
// implements ZeroCopyInputStream ----------------------------------
bool Next(const void** data, int* size) {
// We'll return empty buffers starting with the first buffer, and every
// 3 and 5 buffers after that.
if (counter_ % 3 == 0 || counter_ % 5 == 0) {
*data = NULL;
*size = 0;
++counter_;
return true;
} else {
++counter_;
return array_stream_.Next(data, size);
}
}
void BackUp(int count) { return array_stream_.BackUp(count); }
bool Skip(int count) { return array_stream_.Skip(count); }
int64 ByteCount() const { return array_stream_.ByteCount(); }
private:
ArrayInputStream array_stream_;
int counter_;
};
// -------------------------------------------------------------------
// An error collector which simply concatenates all its errors into a big
// block of text which can be checked.
class TestErrorCollector : public ErrorCollector {
public:
TestErrorCollector() {}
~TestErrorCollector() {}
string text_;
// implements ErrorCollector ---------------------------------------
void AddError(int line, int column, const string& message) {
strings::SubstituteAndAppend(&text_, "$0:$1: $2\n",
line, column, message);
}
};
// -------------------------------------------------------------------
// We test each operation over a variety of block sizes to insure that
// we test cases where reads cross buffer boundaries as well as cases
// where they don't. This is sort of a brute-force approach to this,
// but it's easy to write and easy to understand.
const int kBlockSizes[] = {1, 2, 3, 5, 7, 13, 32, 1024};
class TokenizerTest : public testing::Test {
protected:
// For easy testing.
uint64 ParseInteger(const string& text) {
uint64 result;
EXPECT_TRUE(Tokenizer::ParseInteger(text, kuint64max, &result));
return result;
}
};
// ===================================================================
// These tests causes gcc 3.3.5 (and earlier?) to give the cryptic error:
// "sorry, unimplemented: `method_call_expr' not supported by dump_expr"
#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
// In each test case, the entire input text should parse as a single token
// of the given type.
struct SimpleTokenCase {
string input;
Tokenizer::TokenType type;
};
inline ostream& operator<<(ostream& out,
const SimpleTokenCase& test_case) {
return out << CEscape(test_case.input);
}
SimpleTokenCase kSimpleTokenCases[] = {
// Test identifiers.
{ "hello", Tokenizer::TYPE_IDENTIFIER },
// Test integers.
{ "123", Tokenizer::TYPE_INTEGER },
{ "0xab6", Tokenizer::TYPE_INTEGER },
{ "0XAB6", Tokenizer::TYPE_INTEGER },
{ "0X1234567", Tokenizer::TYPE_INTEGER },
{ "0x89abcdef", Tokenizer::TYPE_INTEGER },
{ "0x89ABCDEF", Tokenizer::TYPE_INTEGER },
{ "01234567", Tokenizer::TYPE_INTEGER },
// Test floats.
{ "123.45", Tokenizer::TYPE_FLOAT },
{ "1.", Tokenizer::TYPE_FLOAT },
{ "1e3", Tokenizer::TYPE_FLOAT },
{ "1E3", Tokenizer::TYPE_FLOAT },
{ "1e-3", Tokenizer::TYPE_FLOAT },
{ "1e+3", Tokenizer::TYPE_FLOAT },
{ "1.e3", Tokenizer::TYPE_FLOAT },
{ "1.2e3", Tokenizer::TYPE_FLOAT },
{ ".1", Tokenizer::TYPE_FLOAT },
{ ".1e3", Tokenizer::TYPE_FLOAT },
{ ".1e-3", Tokenizer::TYPE_FLOAT },
{ ".1e+3", Tokenizer::TYPE_FLOAT },
// Test strings.
{ "'hello'", Tokenizer::TYPE_STRING },
{ "\"foo\"", Tokenizer::TYPE_STRING },
{ "'a\"b'", Tokenizer::TYPE_STRING },
{ "\"a'b\"", Tokenizer::TYPE_STRING },
{ "'a\\'b'", Tokenizer::TYPE_STRING },
{ "\"a\\\"b\"", Tokenizer::TYPE_STRING },
{ "'\\xf'", Tokenizer::TYPE_STRING },
{ "'\\0'", Tokenizer::TYPE_STRING },
// Test symbols.
{ "+", Tokenizer::TYPE_SYMBOL },
{ ".", Tokenizer::TYPE_SYMBOL },
};
TEST_2D(TokenizerTest, SimpleTokens, kSimpleTokenCases, kBlockSizes) {
// Set up the tokenizer.
TestInputStream input(kSimpleTokenCases_case.input.data(),
kSimpleTokenCases_case.input.size(),
kBlockSizes_case);
TestErrorCollector error_collector;
Tokenizer tokenizer(&input, &error_collector);
// Before Next() is called, the initial token should always be TYPE_START.
EXPECT_EQ(Tokenizer::TYPE_START, tokenizer.current().type);
EXPECT_EQ("", tokenizer.current().text);
EXPECT_EQ(0, tokenizer.current().line);
EXPECT_EQ(0, tokenizer.current().column);
// Parse the token.
ASSERT_TRUE(tokenizer.Next());
// Check that it has the right type.
EXPECT_EQ(kSimpleTokenCases_case.type, tokenizer.current().type);
// Check that it contains the complete input text.
EXPECT_EQ(kSimpleTokenCases_case.input, tokenizer.current().text);
// Check that it is located at the beginning of the input
EXPECT_EQ(0, tokenizer.current().line);
EXPECT_EQ(0, tokenizer.current().column);
// There should be no more input.
EXPECT_FALSE(tokenizer.Next());
// After Next() returns false, the token should have type TYPE_END.
EXPECT_EQ(Tokenizer::TYPE_END, tokenizer.current().type);
EXPECT_EQ("", tokenizer.current().text);
EXPECT_EQ(0, tokenizer.current().line);
EXPECT_EQ(kSimpleTokenCases_case.input.size(), tokenizer.current().column);
// There should be no errors.
EXPECT_TRUE(error_collector.text_.empty());
}
TEST_1D(TokenizerTest, FloatSuffix, kBlockSizes) {
// Test the "allow_f_after_float" option.
// Set up the tokenizer.
const char* text = "1f 2.5f 6e3f 7F";
TestInputStream input(text, strlen(text), kBlockSizes_case);
TestErrorCollector error_collector;
Tokenizer tokenizer(&input, &error_collector);
tokenizer.set_allow_f_after_float(true);
// Advance through tokens and check that they are parsed as expected.
ASSERT_TRUE(tokenizer.Next());
EXPECT_EQ(tokenizer.current().text, "1f");
EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT);
ASSERT_TRUE(tokenizer.Next());
EXPECT_EQ(tokenizer.current().text, "2.5f");
EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT);
ASSERT_TRUE(tokenizer.Next());
EXPECT_EQ(tokenizer.current().text, "6e3f");
EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT);
ASSERT_TRUE(tokenizer.Next());
EXPECT_EQ(tokenizer.current().text, "7F");
EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT);
// There should be no more input.
EXPECT_FALSE(tokenizer.Next());
// There should be no errors.
EXPECT_TRUE(error_collector.text_.empty());
}
#endif
// -------------------------------------------------------------------
// In each case, the input is parsed to produce a list of tokens. The
// last token in "output" must have type TYPE_END.
struct MultiTokenCase {
string input;
Tokenizer::Token output[10]; // The compiler wants a constant array
// size for initialization to work. There
// is no reason this can't be increased if
// needed.
};
inline ostream& operator<<(ostream& out,
const MultiTokenCase& test_case) {
return out << CEscape(test_case.input);
}
MultiTokenCase kMultiTokenCases[] = {
// Test empty input.
{ "", {
{ Tokenizer::TYPE_END , "" , 0, 0 },
}},
// Test all token types at the same time.
{ "foo 1 1.2 + 'bar'", {
{ Tokenizer::TYPE_IDENTIFIER, "foo" , 0, 0 },
{ Tokenizer::TYPE_INTEGER , "1" , 0, 4 },
{ Tokenizer::TYPE_FLOAT , "1.2" , 0, 6 },
{ Tokenizer::TYPE_SYMBOL , "+" , 0, 10 },
{ Tokenizer::TYPE_STRING , "'bar'", 0, 12 },
{ Tokenizer::TYPE_END , "" , 0, 17 },
}},
// Test that consecutive symbols are parsed as separate tokens.
{ "!@+%", {
{ Tokenizer::TYPE_SYMBOL , "!" , 0, 0 },
{ Tokenizer::TYPE_SYMBOL , "@" , 0, 1 },
{ Tokenizer::TYPE_SYMBOL , "+" , 0, 2 },
{ Tokenizer::TYPE_SYMBOL , "%" , 0, 3 },
{ Tokenizer::TYPE_END , "" , 0, 4 },
}},
// Test that newlines affect line numbers correctly.
{ "foo bar\nrab oof", {
{ Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 },
{ Tokenizer::TYPE_IDENTIFIER, "bar", 0, 4 },
{ Tokenizer::TYPE_IDENTIFIER, "rab", 1, 0 },
{ Tokenizer::TYPE_IDENTIFIER, "oof", 1, 4 },
{ Tokenizer::TYPE_END , "" , 1, 7 },
}},
// Test that tabs affect column numbers correctly.
{ "foo\tbar \tbaz", {
{ Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 },
{ Tokenizer::TYPE_IDENTIFIER, "bar", 0, 8 },
{ Tokenizer::TYPE_IDENTIFIER, "baz", 0, 16 },
{ Tokenizer::TYPE_END , "" , 0, 19 },
}},
// Test that line comments are ignored.
{ "foo // This is a comment\n"
"bar // This is another comment", {
{ Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 },
{ Tokenizer::TYPE_IDENTIFIER, "bar", 1, 0 },
{ Tokenizer::TYPE_END , "" , 1, 30 },
}},
// Test that block comments are ignored.
{ "foo /* This is a block comment */ bar", {
{ Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 },
{ Tokenizer::TYPE_IDENTIFIER, "bar", 0, 34 },
{ Tokenizer::TYPE_END , "" , 0, 37 },
}},
// Test that sh-style comments are not ignored by default.
{ "foo # bar\n"
"baz", {
{ Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 },
{ Tokenizer::TYPE_SYMBOL , "#" , 0, 4 },
{ Tokenizer::TYPE_IDENTIFIER, "bar", 0, 6 },
{ Tokenizer::TYPE_IDENTIFIER, "baz", 1, 0 },
{ Tokenizer::TYPE_END , "" , 1, 3 },
}},
// Bytes with the high-order bit set should not be seen as control characters.
{ "\300", {
{ Tokenizer::TYPE_SYMBOL, "\300", 0, 0 },
{ Tokenizer::TYPE_END , "" , 0, 1 },
}},
// Test all whitespace chars
{ "foo\n\t\r\v\fbar", {
{ Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 },
{ Tokenizer::TYPE_IDENTIFIER, "bar", 1, 11 },
{ Tokenizer::TYPE_END , "" , 1, 14 },
}},
};
TEST_2D(TokenizerTest, MultipleTokens, kMultiTokenCases, kBlockSizes) {
// Set up the tokenizer.
TestInputStream input(kMultiTokenCases_case.input.data(),
kMultiTokenCases_case.input.size(),
kBlockSizes_case);
TestErrorCollector error_collector;
Tokenizer tokenizer(&input, &error_collector);
// Before Next() is called, the initial token should always be TYPE_START.
EXPECT_EQ(Tokenizer::TYPE_START, tokenizer.current().type);
EXPECT_EQ("", tokenizer.current().text);
EXPECT_EQ(0, tokenizer.current().line);
EXPECT_EQ(0, tokenizer.current().column);
// Loop through all expected tokens.
int i = 0;
Tokenizer::Token token;
do {
token = kMultiTokenCases_case.output[i++];
SCOPED_TRACE(testing::Message() << "Token #" << i << ": " << token.text);
// Next() should only return false when it hits the end token.
if (token.type != Tokenizer::TYPE_END) {
ASSERT_TRUE(tokenizer.Next());
} else {
ASSERT_FALSE(tokenizer.Next());
}
// Check that the token matches the expected one.
EXPECT_EQ(token.type, tokenizer.current().type);
EXPECT_EQ(token.text, tokenizer.current().text);
EXPECT_EQ(token.line, tokenizer.current().line);
EXPECT_EQ(token.column, tokenizer.current().column);
} while (token.type != Tokenizer::TYPE_END);
// There should be no errors.
EXPECT_TRUE(error_collector.text_.empty());
}
// This test causes gcc 3.3.5 (and earlier?) to give the cryptic error:
// "sorry, unimplemented: `method_call_expr' not supported by dump_expr"
#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
TEST_1D(TokenizerTest, ShCommentStyle, kBlockSizes) {
// Test the "comment_style" option.
const char* text = "foo # bar\n"
"baz // qux\n"
"corge /* grault */\n"
"garply";
const char* const kTokens[] = {"foo", // "# bar" is ignored
"baz", "/", "/", "qux",
"corge", "/", "*", "grault", "*", "/",
"garply"};
// Set up the tokenizer.
TestInputStream input(text, strlen(text), kBlockSizes_case);
TestErrorCollector error_collector;
Tokenizer tokenizer(&input, &error_collector);
tokenizer.set_comment_style(Tokenizer::SH_COMMENT_STYLE);
// Advance through tokens and check that they are parsed as expected.
for (int i = 0; i < GOOGLE_ARRAYSIZE(kTokens); i++) {
EXPECT_TRUE(tokenizer.Next());
EXPECT_EQ(tokenizer.current().text, kTokens[i]);
}
// There should be no more input.
EXPECT_FALSE(tokenizer.Next());
// There should be no errors.
EXPECT_TRUE(error_collector.text_.empty());
}
#endif
// -------------------------------------------------------------------
// Test parse helpers. It's not really worth setting up a full data-driven
// test here.
TEST_F(TokenizerTest, ParseInteger) {
EXPECT_EQ(0, ParseInteger("0"));
EXPECT_EQ(123, ParseInteger("123"));
EXPECT_EQ(0xabcdef12u, ParseInteger("0xabcdef12"));
EXPECT_EQ(0xabcdef12u, ParseInteger("0xABCDEF12"));
EXPECT_EQ(kuint64max, ParseInteger("0xFFFFFFFFFFFFFFFF"));
EXPECT_EQ(01234567, ParseInteger("01234567"));
EXPECT_EQ(0X123, ParseInteger("0X123"));
// Test invalid integers that may still be tokenized as integers.
EXPECT_EQ(0, ParseInteger("0x"));
uint64 i;
#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet
// Test invalid integers that will never be tokenized as integers.
EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("zxy", kuint64max, &i),
"passed text that could not have been tokenized as an integer");
EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("1.2", kuint64max, &i),
"passed text that could not have been tokenized as an integer");
EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("08", kuint64max, &i),
"passed text that could not have been tokenized as an integer");
EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("0xg", kuint64max, &i),
"passed text that could not have been tokenized as an integer");
EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("-1", kuint64max, &i),
"passed text that could not have been tokenized as an integer");
#endif // GTEST_HAS_DEATH_TEST
// Test overflows.
EXPECT_TRUE (Tokenizer::ParseInteger("0", 0, &i));
EXPECT_FALSE(Tokenizer::ParseInteger("1", 0, &i));
EXPECT_TRUE (Tokenizer::ParseInteger("1", 1, &i));
EXPECT_TRUE (Tokenizer::ParseInteger("12345", 12345, &i));
EXPECT_FALSE(Tokenizer::ParseInteger("12346", 12345, &i));
EXPECT_TRUE (Tokenizer::ParseInteger("0xFFFFFFFFFFFFFFFF" , kuint64max, &i));
EXPECT_FALSE(Tokenizer::ParseInteger("0x10000000000000000", kuint64max, &i));
}
TEST_F(TokenizerTest, ParseFloat) {
EXPECT_DOUBLE_EQ(1 , Tokenizer::ParseFloat("1."));
EXPECT_DOUBLE_EQ(1e3 , Tokenizer::ParseFloat("1e3"));
EXPECT_DOUBLE_EQ(1e3 , Tokenizer::ParseFloat("1E3"));
EXPECT_DOUBLE_EQ(1.5e3, Tokenizer::ParseFloat("1.5e3"));
EXPECT_DOUBLE_EQ(.1 , Tokenizer::ParseFloat(".1"));
EXPECT_DOUBLE_EQ(.25 , Tokenizer::ParseFloat(".25"));
EXPECT_DOUBLE_EQ(.1e3 , Tokenizer::ParseFloat(".1e3"));
EXPECT_DOUBLE_EQ(.25e3, Tokenizer::ParseFloat(".25e3"));
EXPECT_DOUBLE_EQ(.1e+3, Tokenizer::ParseFloat(".1e+3"));
EXPECT_DOUBLE_EQ(.1e-3, Tokenizer::ParseFloat(".1e-3"));
EXPECT_DOUBLE_EQ(5 , Tokenizer::ParseFloat("5"));
EXPECT_DOUBLE_EQ(6e-12, Tokenizer::ParseFloat("6e-12"));
EXPECT_DOUBLE_EQ(1.2 , Tokenizer::ParseFloat("1.2"));
EXPECT_DOUBLE_EQ(1.e2 , Tokenizer::ParseFloat("1.e2"));
// Test invalid integers that may still be tokenized as integers.
EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1e"));
EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1e-"));
EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1.e"));
// Test 'f' suffix.
EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1f"));
EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1.0f"));
EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1F"));
// These should parse successfully even though they are out of range.
// Overflows become infinity and underflows become zero.
EXPECT_EQ( 0.0, Tokenizer::ParseFloat("1e-9999999999999999999999999999"));
EXPECT_EQ(HUGE_VAL, Tokenizer::ParseFloat("1e+9999999999999999999999999999"));
#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet
// Test invalid integers that will never be tokenized as integers.
EXPECT_DEBUG_DEATH(Tokenizer::ParseFloat("zxy"),
"passed text that could not have been tokenized as a float");
EXPECT_DEBUG_DEATH(Tokenizer::ParseFloat("1-e0"),
"passed text that could not have been tokenized as a float");
EXPECT_DEBUG_DEATH(Tokenizer::ParseFloat("-1.0"),
"passed text that could not have been tokenized as a float");
#endif // GTEST_HAS_DEATH_TEST
}
TEST_F(TokenizerTest, ParseString) {
string output;
Tokenizer::ParseString("'hello'", &output);
EXPECT_EQ("hello", output);
Tokenizer::ParseString("\"blah\\nblah2\"", &output);
EXPECT_EQ("blah\nblah2", output);
Tokenizer::ParseString("'\\1x\\1\\123\\739\\52\\334n\\3'", &output);
EXPECT_EQ("\1x\1\123\739\52\334n\3", output);
Tokenizer::ParseString("'\\x20\\x4'", &output);
EXPECT_EQ("\x20\x4", output);
// Test invalid strings that may still be tokenized as strings.
Tokenizer::ParseString("\"\\a\\l\\v\\t", &output); // \l is invalid
EXPECT_EQ("\a?\v\t", output);
Tokenizer::ParseString("'", &output);
EXPECT_EQ("", output);
Tokenizer::ParseString("'\\", &output);
EXPECT_EQ("\\", output);
// Test invalid strings that will never be tokenized as strings.
#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet
EXPECT_DEBUG_DEATH(Tokenizer::ParseString("", &output),
"passed text that could not have been tokenized as a string");
#endif // GTEST_HAS_DEATH_TEST
}
TEST_F(TokenizerTest, ParseStringAppend) {
// Check that ParseString and ParseStringAppend differ.
string output("stuff+");
Tokenizer::ParseStringAppend("'hello'", &output);
EXPECT_EQ("stuff+hello", output);
Tokenizer::ParseString("'hello'", &output);
EXPECT_EQ("hello", output);
}
// -------------------------------------------------------------------
// Each case parses some input text, ignoring the tokens produced, and
// checks that the error output matches what is expected.
struct ErrorCase {
string input;
bool recoverable; // True if the tokenizer should be able to recover and
// parse more tokens after seeing this error. Cases
// for which this is true must end with "foo" as
// the last token, which the test will check for.
const char* errors;
};
inline ostream& operator<<(ostream& out,
const ErrorCase& test_case) {
return out << CEscape(test_case.input);
}
ErrorCase kErrorCases[] = {
// String errors.
{ "'\\l' foo", true,
"0:2: Invalid escape sequence in string literal.\n" },
{ "'\\x' foo", true,
"0:3: Expected hex digits for escape sequence.\n" },
{ "'foo", false,
"0:4: String literals cannot cross line boundaries.\n" },
{ "'bar\nfoo", true,
"0:4: String literals cannot cross line boundaries.\n" },
// Integer errors.
{ "123foo", true,
"0:3: Need space between number and identifier.\n" },
// Hex/octal errors.
{ "0x foo", true,
"0:2: \"0x\" must be followed by hex digits.\n" },
{ "0541823 foo", true,
"0:4: Numbers starting with leading zero must be in octal.\n" },
{ "0x123z foo", true,
"0:5: Need space between number and identifier.\n" },
{ "0x123.4 foo", true,
"0:5: Hex and octal numbers must be integers.\n" },
{ "0123.4 foo", true,
"0:4: Hex and octal numbers must be integers.\n" },
// Float errors.
{ "1e foo", true,
"0:2: \"e\" must be followed by exponent.\n" },
{ "1e- foo", true,
"0:3: \"e\" must be followed by exponent.\n" },
{ "1.2.3 foo", true,
"0:3: Already saw decimal point or exponent; can't have another one.\n" },
{ "1e2.3 foo", true,
"0:3: Already saw decimal point or exponent; can't have another one.\n" },
{ "a.1 foo", true,
"0:1: Need space between identifier and decimal point.\n" },
// allow_f_after_float not enabled, so this should be an error.
{ "1.0f foo", true,
"0:3: Need space between number and identifier.\n" },
// Block comment errors.
{ "/*", false,
"0:2: End-of-file inside block comment.\n"
"0:0: Comment started here.\n"},
{ "/*/*/ foo", true,
"0:3: \"/*\" inside block comment. Block comments cannot be nested.\n"},
// Control characters. Multiple consecutive control characters should only
// produce one error.
{ "\b foo", true,
"0:0: Invalid control characters encountered in text.\n" },
{ "\b\b foo", true,
"0:0: Invalid control characters encountered in text.\n" },
// Check that control characters at end of input don't result in an
// infinite loop.
{ "\b", false,
"0:0: Invalid control characters encountered in text.\n" },
// Check recovery from '\0'. We have to explicitly specify the length of
// these strings because otherwise the string constructor will just call
// strlen() which will see the first '\0' and think that is the end of the
// string.
{ string("\0foo", 4), true,
"0:0: Invalid control characters encountered in text.\n" },
{ string("\0\0foo", 5), true,
"0:0: Invalid control characters encountered in text.\n" },
};
TEST_2D(TokenizerTest, Errors, kErrorCases, kBlockSizes) {
// Set up the tokenizer.
TestInputStream input(kErrorCases_case.input.data(),
kErrorCases_case.input.size(),
kBlockSizes_case);
TestErrorCollector error_collector;
Tokenizer tokenizer(&input, &error_collector);
// Ignore all input, except remember if the last token was "foo".
bool last_was_foo = false;
while (tokenizer.Next()) {
last_was_foo = tokenizer.current().text == "foo";
}
// Check that the errors match what was expected.
EXPECT_EQ(error_collector.text_, kErrorCases_case.errors);
// If the error was recoverable, make sure we saw "foo" after it.
if (kErrorCases_case.recoverable) {
EXPECT_TRUE(last_was_foo);
}
}
// -------------------------------------------------------------------
TEST_1D(TokenizerTest, BackUpOnDestruction, kBlockSizes) {
string text = "foo bar";
TestInputStream input(text.data(), text.size(), kBlockSizes_case);
// Create a tokenizer, read one token, then destroy it.
{
TestErrorCollector error_collector;
Tokenizer tokenizer(&input, &error_collector);
tokenizer.Next();
}
// Only "foo" should have been read.
EXPECT_EQ(strlen("foo"), input.ByteCount());
}
} // namespace
} // namespace io
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,48 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/io/zero_copy_stream.h>
namespace google {
namespace protobuf {
namespace io {
ZeroCopyInputStream::~ZeroCopyInputStream() {}
ZeroCopyOutputStream::~ZeroCopyOutputStream() {}
} // namespace io
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,238 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This file contains the ZeroCopyInputStream and ZeroCopyOutputStream
// interfaces, which represent abstract I/O streams to and from which
// protocol buffers can be read and written. For a few simple
// implementations of these interfaces, see zero_copy_stream_impl.h.
//
// These interfaces are different from classic I/O streams in that they
// try to minimize the amount of data copying that needs to be done.
// To accomplish this, responsibility for allocating buffers is moved to
// the stream object, rather than being the responsibility of the caller.
// So, the stream can return a buffer which actually points directly into
// the final data structure where the bytes are to be stored, and the caller
// can interact directly with that buffer, eliminating an intermediate copy
// operation.
//
// As an example, consider the common case in which you are reading bytes
// from an array that is already in memory (or perhaps an mmap()ed file).
// With classic I/O streams, you would do something like:
// char buffer[BUFFER_SIZE];
// input->Read(buffer, BUFFER_SIZE);
// DoSomething(buffer, BUFFER_SIZE);
// Then, the stream basically just calls memcpy() to copy the data from
// the array into your buffer. With a ZeroCopyInputStream, you would do
// this instead:
// const void* buffer;
// int size;
// input->Next(&buffer, &size);
// DoSomething(buffer, size);
// Here, no copy is performed. The input stream returns a pointer directly
// into the backing array, and the caller ends up reading directly from it.
//
// If you want to be able to read the old-fashion way, you can create
// a CodedInputStream or CodedOutputStream wrapping these objects and use
// their ReadRaw()/WriteRaw() methods. These will, of course, add a copy
// step, but Coded*Stream will handle buffering so at least it will be
// reasonably efficient.
//
// ZeroCopyInputStream example:
// // Read in a file and print its contents to stdout.
// int fd = open("myfile", O_RDONLY);
// ZeroCopyInputStream* input = new FileInputStream(fd);
//
// const void* buffer;
// int size;
// while (input->Next(&buffer, &size)) {
// cout.write(buffer, size);
// }
//
// delete input;
// close(fd);
//
// ZeroCopyOutputStream example:
// // Copy the contents of "infile" to "outfile", using plain read() for
// // "infile" but a ZeroCopyOutputStream for "outfile".
// int infd = open("infile", O_RDONLY);
// int outfd = open("outfile", O_WRONLY);
// ZeroCopyOutputStream* output = new FileOutputStream(outfd);
//
// void* buffer;
// int size;
// while (output->Next(&buffer, &size)) {
// int bytes = read(infd, buffer, size);
// if (bytes < size) {
// // Reached EOF.
// output->BackUp(size - bytes);
// break;
// }
// }
//
// delete output;
// close(infd);
// close(outfd);
#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__
#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__
#include <string>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
namespace io {
// Defined in this file.
class ZeroCopyInputStream;
class ZeroCopyOutputStream;
// Abstract interface similar to an input stream but designed to minimize
// copying.
class LIBPROTOBUF_EXPORT ZeroCopyInputStream {
public:
inline ZeroCopyInputStream() {}
virtual ~ZeroCopyInputStream();
// Obtains a chunk of data from the stream.
//
// Preconditions:
// * "size" and "data" are not NULL.
//
// Postconditions:
// * If the returned value is false, there is no more data to return or
// an error occurred. All errors are permanent.
// * Otherwise, "size" points to the actual number of bytes read and "data"
// points to a pointer to a buffer containing these bytes.
// * Ownership of this buffer remains with the stream, and the buffer
// remains valid only until some other method of the stream is called
// or the stream is destroyed.
// * It is legal for the returned buffer to have zero size, as long
// as repeatedly calling Next() eventually yields a buffer with non-zero
// size.
virtual bool Next(const void** data, int* size) = 0;
// Backs up a number of bytes, so that the next call to Next() returns
// data again that was already returned by the last call to Next(). This
// is useful when writing procedures that are only supposed to read up
// to a certain point in the input, then return. If Next() returns a
// buffer that goes beyond what you wanted to read, you can use BackUp()
// to return to the point where you intended to finish.
//
// Preconditions:
// * The last method called must have been Next().
// * count must be less than or equal to the size of the last buffer
// returned by Next().
//
// Postconditions:
// * The last "count" bytes of the last buffer returned by Next() will be
// pushed back into the stream. Subsequent calls to Next() will return
// the same data again before producing new data.
virtual void BackUp(int count) = 0;
// Skips a number of bytes. Returns false if the end of the stream is
// reached or some input error occurred. In the end-of-stream case, the
// stream is advanced to the end of the stream (so ByteCount() will return
// the total size of the stream).
virtual bool Skip(int count) = 0;
// Returns the total number of bytes read since this object was created.
virtual int64 ByteCount() const = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyInputStream);
};
// Abstract interface similar to an output stream but designed to minimize
// copying.
class LIBPROTOBUF_EXPORT ZeroCopyOutputStream {
public:
inline ZeroCopyOutputStream() {}
virtual ~ZeroCopyOutputStream();
// Obtains a buffer into which data can be written. Any data written
// into this buffer will eventually (maybe instantly, maybe later on)
// be written to the output.
//
// Preconditions:
// * "size" and "data" are not NULL.
//
// Postconditions:
// * If the returned value is false, an error occurred. All errors are
// permanent.
// * Otherwise, "size" points to the actual number of bytes in the buffer
// and "data" points to the buffer.
// * Ownership of this buffer remains with the stream, and the buffer
// remains valid only until some other method of the stream is called
// or the stream is destroyed.
// * Any data which the caller stores in this buffer will eventually be
// written to the output (unless BackUp() is called).
// * It is legal for the returned buffer to have zero size, as long
// as repeatedly calling Next() eventually yields a buffer with non-zero
// size.
virtual bool Next(void** data, int* size) = 0;
// Backs up a number of bytes, so that the end of the last buffer returned
// by Next() is not actually written. This is needed when you finish
// writing all the data you want to write, but the last buffer was bigger
// than you needed. You don't want to write a bunch of garbage after the
// end of your data, so you use BackUp() to back up.
//
// Preconditions:
// * The last method called must have been Next().
// * count must be less than or equal to the size of the last buffer
// returned by Next().
// * The caller must not have written anything to the last "count" bytes
// of that buffer.
//
// Postconditions:
// * The last "count" bytes of the last buffer returned by Next() will be
// ignored.
virtual void BackUp(int count) = 0;
// Returns the total number of bytes written since this object was created.
virtual int64 ByteCount() const = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyOutputStream);
};
} // namespace io
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__

View file

@ -0,0 +1,470 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifdef _MSC_VER
#include <io.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#include <errno.h>
#include <iostream>
#include <algorithm>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
namespace protobuf {
namespace io {
#ifdef _WIN32
// Win32 lseek is broken: If invoked on a non-seekable file descriptor, its
// return value is undefined. We re-define it to always produce an error.
#define lseek(fd, offset, origin) ((off_t)-1)
#endif
namespace {
// EINTR sucks.
int close_no_eintr(int fd) {
int result;
do {
result = close(fd);
} while (result < 0 && errno == EINTR);
return result;
}
} // namespace
// ===================================================================
FileInputStream::FileInputStream(int file_descriptor, int block_size)
: copying_input_(file_descriptor),
impl_(&copying_input_, block_size) {
}
FileInputStream::~FileInputStream() {}
bool FileInputStream::Close() {
return copying_input_.Close();
}
bool FileInputStream::Next(const void** data, int* size) {
return impl_.Next(data, size);
}
void FileInputStream::BackUp(int count) {
impl_.BackUp(count);
}
bool FileInputStream::Skip(int count) {
return impl_.Skip(count);
}
int64 FileInputStream::ByteCount() const {
return impl_.ByteCount();
}
FileInputStream::CopyingFileInputStream::CopyingFileInputStream(
int file_descriptor)
: file_(file_descriptor),
close_on_delete_(false),
is_closed_(false),
errno_(0),
previous_seek_failed_(false) {
}
FileInputStream::CopyingFileInputStream::~CopyingFileInputStream() {
if (close_on_delete_) {
if (!Close()) {
GOOGLE_LOG(ERROR) << "close() failed: " << strerror(errno_);
}
}
}
bool FileInputStream::CopyingFileInputStream::Close() {
GOOGLE_CHECK(!is_closed_);
is_closed_ = true;
if (close_no_eintr(file_) != 0) {
// The docs on close() do not specify whether a file descriptor is still
// open after close() fails with EIO. However, the glibc source code
// seems to indicate that it is not.
errno_ = errno;
return false;
}
return true;
}
int FileInputStream::CopyingFileInputStream::Read(void* buffer, int size) {
GOOGLE_CHECK(!is_closed_);
int result;
do {
result = read(file_, buffer, size);
} while (result < 0 && errno == EINTR);
if (result < 0) {
// Read error (not EOF).
errno_ = errno;
}
return result;
}
int FileInputStream::CopyingFileInputStream::Skip(int count) {
GOOGLE_CHECK(!is_closed_);
if (!previous_seek_failed_ &&
lseek(file_, count, SEEK_CUR) != (off_t)-1) {
// Seek succeeded.
return count;
} else {
// Failed to seek.
// Note to self: Don't seek again. This file descriptor doesn't
// support it.
previous_seek_failed_ = true;
// Use the default implementation.
return CopyingInputStream::Skip(count);
}
}
// ===================================================================
FileOutputStream::FileOutputStream(int file_descriptor, int block_size)
: copying_output_(file_descriptor),
impl_(&copying_output_, block_size) {
}
FileOutputStream::~FileOutputStream() {
impl_.Flush();
}
bool FileOutputStream::Close() {
bool flush_succeeded = impl_.Flush();
return copying_output_.Close() && flush_succeeded;
}
bool FileOutputStream::Flush() {
return impl_.Flush();
}
bool FileOutputStream::Next(void** data, int* size) {
return impl_.Next(data, size);
}
void FileOutputStream::BackUp(int count) {
impl_.BackUp(count);
}
int64 FileOutputStream::ByteCount() const {
return impl_.ByteCount();
}
FileOutputStream::CopyingFileOutputStream::CopyingFileOutputStream(
int file_descriptor)
: file_(file_descriptor),
close_on_delete_(false),
is_closed_(false),
errno_(0) {
}
FileOutputStream::CopyingFileOutputStream::~CopyingFileOutputStream() {
if (close_on_delete_) {
if (!Close()) {
GOOGLE_LOG(ERROR) << "close() failed: " << strerror(errno_);
}
}
}
bool FileOutputStream::CopyingFileOutputStream::Close() {
GOOGLE_CHECK(!is_closed_);
is_closed_ = true;
if (close_no_eintr(file_) != 0) {
// The docs on close() do not specify whether a file descriptor is still
// open after close() fails with EIO. However, the glibc source code
// seems to indicate that it is not.
errno_ = errno;
return false;
}
return true;
}
bool FileOutputStream::CopyingFileOutputStream::Write(
const void* buffer, int size) {
GOOGLE_CHECK(!is_closed_);
int total_written = 0;
const uint8* buffer_base = reinterpret_cast<const uint8*>(buffer);
while (total_written < size) {
int bytes;
do {
bytes = write(file_, buffer_base + total_written, size - total_written);
} while (bytes < 0 && errno == EINTR);
if (bytes <= 0) {
// Write error.
// FIXME(kenton): According to the man page, if write() returns zero,
// there was no error; write() simply did not write anything. It's
// unclear under what circumstances this might happen, but presumably
// errno won't be set in this case. I am confused as to how such an
// event should be handled. For now I'm treating it as an error, since
// retrying seems like it could lead to an infinite loop. I suspect
// this never actually happens anyway.
if (bytes < 0) {
errno_ = errno;
}
return false;
}
total_written += bytes;
}
return true;
}
// ===================================================================
IstreamInputStream::IstreamInputStream(istream* input, int block_size)
: copying_input_(input),
impl_(&copying_input_, block_size) {
}
IstreamInputStream::~IstreamInputStream() {}
bool IstreamInputStream::Next(const void** data, int* size) {
return impl_.Next(data, size);
}
void IstreamInputStream::BackUp(int count) {
impl_.BackUp(count);
}
bool IstreamInputStream::Skip(int count) {
return impl_.Skip(count);
}
int64 IstreamInputStream::ByteCount() const {
return impl_.ByteCount();
}
IstreamInputStream::CopyingIstreamInputStream::CopyingIstreamInputStream(
istream* input)
: input_(input) {
}
IstreamInputStream::CopyingIstreamInputStream::~CopyingIstreamInputStream() {}
int IstreamInputStream::CopyingIstreamInputStream::Read(
void* buffer, int size) {
input_->read(reinterpret_cast<char*>(buffer), size);
int result = input_->gcount();
if (result == 0 && input_->fail() && !input_->eof()) {
return -1;
}
return result;
}
// ===================================================================
OstreamOutputStream::OstreamOutputStream(ostream* output, int block_size)
: copying_output_(output),
impl_(&copying_output_, block_size) {
}
OstreamOutputStream::~OstreamOutputStream() {
impl_.Flush();
}
bool OstreamOutputStream::Next(void** data, int* size) {
return impl_.Next(data, size);
}
void OstreamOutputStream::BackUp(int count) {
impl_.BackUp(count);
}
int64 OstreamOutputStream::ByteCount() const {
return impl_.ByteCount();
}
OstreamOutputStream::CopyingOstreamOutputStream::CopyingOstreamOutputStream(
ostream* output)
: output_(output) {
}
OstreamOutputStream::CopyingOstreamOutputStream::~CopyingOstreamOutputStream() {
}
bool OstreamOutputStream::CopyingOstreamOutputStream::Write(
const void* buffer, int size) {
output_->write(reinterpret_cast<const char*>(buffer), size);
return output_->good();
}
// ===================================================================
ConcatenatingInputStream::ConcatenatingInputStream(
ZeroCopyInputStream* const streams[], int count)
: streams_(streams), stream_count_(count), bytes_retired_(0) {
}
ConcatenatingInputStream::~ConcatenatingInputStream() {
}
bool ConcatenatingInputStream::Next(const void** data, int* size) {
while (stream_count_ > 0) {
if (streams_[0]->Next(data, size)) return true;
// That stream is done. Advance to the next one.
bytes_retired_ += streams_[0]->ByteCount();
++streams_;
--stream_count_;
}
// No more streams.
return false;
}
void ConcatenatingInputStream::BackUp(int count) {
if (stream_count_ > 0) {
streams_[0]->BackUp(count);
} else {
GOOGLE_LOG(DFATAL) << "Can't BackUp() after failed Next().";
}
}
bool ConcatenatingInputStream::Skip(int count) {
while (stream_count_ > 0) {
// Assume that ByteCount() can be used to find out how much we actually
// skipped when Skip() fails.
int64 target_byte_count = streams_[0]->ByteCount() + count;
if (streams_[0]->Skip(count)) return true;
// Hit the end of the stream. Figure out how many more bytes we still have
// to skip.
int64 final_byte_count = streams_[0]->ByteCount();
GOOGLE_DCHECK_LT(final_byte_count, target_byte_count);
count = target_byte_count - final_byte_count;
// That stream is done. Advance to the next one.
bytes_retired_ += final_byte_count;
++streams_;
--stream_count_;
}
return false;
}
int64 ConcatenatingInputStream::ByteCount() const {
if (stream_count_ == 0) {
return bytes_retired_;
} else {
return bytes_retired_ + streams_[0]->ByteCount();
}
}
// ===================================================================
LimitingInputStream::LimitingInputStream(ZeroCopyInputStream* input,
int64 limit)
: input_(input), limit_(limit) {}
LimitingInputStream::~LimitingInputStream() {
// If we overshot the limit, back up.
if (limit_ < 0) input_->BackUp(-limit_);
}
bool LimitingInputStream::Next(const void** data, int* size) {
if (limit_ <= 0) return false;
if (!input_->Next(data, size)) return false;
limit_ -= *size;
if (limit_ < 0) {
// We overshot the limit. Reduce *size to hide the rest of the buffer.
*size += limit_;
}
return true;
}
void LimitingInputStream::BackUp(int count) {
if (limit_ < 0) {
input_->BackUp(count - limit_);
limit_ = count;
} else {
input_->BackUp(count);
limit_ += count;
}
}
bool LimitingInputStream::Skip(int count) {
if (count > limit_) {
if (limit_ < 0) return false;
input_->Skip(limit_);
limit_ = 0;
return false;
} else {
if (!input_->Skip(count)) return false;
limit_ -= count;
return true;
}
}
int64 LimitingInputStream::ByteCount() const {
if (limit_ < 0) {
return input_->ByteCount() + limit_;
} else {
return input_->ByteCount();
}
}
// ===================================================================
} // namespace io
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,357 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This file contains common implementations of the interfaces defined in
// zero_copy_stream.h which are only included in the full (non-lite)
// protobuf library. These implementations include Unix file descriptors
// and C++ iostreams. See also: zero_copy_stream_impl_lite.h
#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__
#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__
#include <string>
#include <iosfwd>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
namespace io {
// ===================================================================
// A ZeroCopyInputStream which reads from a file descriptor.
//
// FileInputStream is preferred over using an ifstream with IstreamInputStream.
// The latter will introduce an extra layer of buffering, harming performance.
// Also, it's conceivable that FileInputStream could someday be enhanced
// to use zero-copy file descriptors on OSs which support them.
class LIBPROTOBUF_EXPORT FileInputStream : public ZeroCopyInputStream {
public:
// Creates a stream that reads from the given Unix file descriptor.
// If a block_size is given, it specifies the number of bytes that
// should be read and returned with each call to Next(). Otherwise,
// a reasonable default is used.
explicit FileInputStream(int file_descriptor, int block_size = -1);
~FileInputStream();
// Flushes any buffers and closes the underlying file. Returns false if
// an error occurs during the process; use GetErrno() to examine the error.
// Even if an error occurs, the file descriptor is closed when this returns.
bool Close();
// By default, the file descriptor is not closed when the stream is
// destroyed. Call SetCloseOnDelete(true) to change that. WARNING:
// This leaves no way for the caller to detect if close() fails. If
// detecting close() errors is important to you, you should arrange
// to close the descriptor yourself.
void SetCloseOnDelete(bool value) { copying_input_.SetCloseOnDelete(value); }
// If an I/O error has occurred on this file descriptor, this is the
// errno from that error. Otherwise, this is zero. Once an error
// occurs, the stream is broken and all subsequent operations will
// fail.
int GetErrno() { return copying_input_.GetErrno(); }
// implements ZeroCopyInputStream ----------------------------------
bool Next(const void** data, int* size);
void BackUp(int count);
bool Skip(int count);
int64 ByteCount() const;
private:
class LIBPROTOBUF_EXPORT CopyingFileInputStream : public CopyingInputStream {
public:
CopyingFileInputStream(int file_descriptor);
~CopyingFileInputStream();
bool Close();
void SetCloseOnDelete(bool value) { close_on_delete_ = value; }
int GetErrno() { return errno_; }
// implements CopyingInputStream ---------------------------------
int Read(void* buffer, int size);
int Skip(int count);
private:
// The file descriptor.
const int file_;
bool close_on_delete_;
bool is_closed_;
// The errno of the I/O error, if one has occurred. Otherwise, zero.
int errno_;
// Did we try to seek once and fail? If so, we assume this file descriptor
// doesn't support seeking and won't try again.
bool previous_seek_failed_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingFileInputStream);
};
CopyingFileInputStream copying_input_;
CopyingInputStreamAdaptor impl_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileInputStream);
};
// ===================================================================
// A ZeroCopyOutputStream which writes to a file descriptor.
//
// FileOutputStream is preferred over using an ofstream with
// OstreamOutputStream. The latter will introduce an extra layer of buffering,
// harming performance. Also, it's conceivable that FileOutputStream could
// someday be enhanced to use zero-copy file descriptors on OSs which
// support them.
class LIBPROTOBUF_EXPORT FileOutputStream : public ZeroCopyOutputStream {
public:
// Creates a stream that writes to the given Unix file descriptor.
// If a block_size is given, it specifies the size of the buffers
// that should be returned by Next(). Otherwise, a reasonable default
// is used.
explicit FileOutputStream(int file_descriptor, int block_size = -1);
~FileOutputStream();
// Flushes any buffers and closes the underlying file. Returns false if
// an error occurs during the process; use GetErrno() to examine the error.
// Even if an error occurs, the file descriptor is closed when this returns.
bool Close();
// Flushes FileOutputStream's buffers but does not close the
// underlying file. No special measures are taken to ensure that
// underlying operating system file object is synchronized to disk.
bool Flush();
// By default, the file descriptor is not closed when the stream is
// destroyed. Call SetCloseOnDelete(true) to change that. WARNING:
// This leaves no way for the caller to detect if close() fails. If
// detecting close() errors is important to you, you should arrange
// to close the descriptor yourself.
void SetCloseOnDelete(bool value) { copying_output_.SetCloseOnDelete(value); }
// If an I/O error has occurred on this file descriptor, this is the
// errno from that error. Otherwise, this is zero. Once an error
// occurs, the stream is broken and all subsequent operations will
// fail.
int GetErrno() { return copying_output_.GetErrno(); }
// implements ZeroCopyOutputStream ---------------------------------
bool Next(void** data, int* size);
void BackUp(int count);
int64 ByteCount() const;
private:
class LIBPROTOBUF_EXPORT CopyingFileOutputStream : public CopyingOutputStream {
public:
CopyingFileOutputStream(int file_descriptor);
~CopyingFileOutputStream();
bool Close();
void SetCloseOnDelete(bool value) { close_on_delete_ = value; }
int GetErrno() { return errno_; }
// implements CopyingOutputStream --------------------------------
bool Write(const void* buffer, int size);
private:
// The file descriptor.
const int file_;
bool close_on_delete_;
bool is_closed_;
// The errno of the I/O error, if one has occurred. Otherwise, zero.
int errno_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingFileOutputStream);
};
CopyingFileOutputStream copying_output_;
CopyingOutputStreamAdaptor impl_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileOutputStream);
};
// ===================================================================
// A ZeroCopyInputStream which reads from a C++ istream.
//
// Note that for reading files (or anything represented by a file descriptor),
// FileInputStream is more efficient.
class LIBPROTOBUF_EXPORT IstreamInputStream : public ZeroCopyInputStream {
public:
// Creates a stream that reads from the given C++ istream.
// If a block_size is given, it specifies the number of bytes that
// should be read and returned with each call to Next(). Otherwise,
// a reasonable default is used.
explicit IstreamInputStream(istream* stream, int block_size = -1);
~IstreamInputStream();
// implements ZeroCopyInputStream ----------------------------------
bool Next(const void** data, int* size);
void BackUp(int count);
bool Skip(int count);
int64 ByteCount() const;
private:
class LIBPROTOBUF_EXPORT CopyingIstreamInputStream : public CopyingInputStream {
public:
CopyingIstreamInputStream(istream* input);
~CopyingIstreamInputStream();
// implements CopyingInputStream ---------------------------------
int Read(void* buffer, int size);
// (We use the default implementation of Skip().)
private:
// The stream.
istream* input_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingIstreamInputStream);
};
CopyingIstreamInputStream copying_input_;
CopyingInputStreamAdaptor impl_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(IstreamInputStream);
};
// ===================================================================
// A ZeroCopyOutputStream which writes to a C++ ostream.
//
// Note that for writing files (or anything represented by a file descriptor),
// FileOutputStream is more efficient.
class LIBPROTOBUF_EXPORT OstreamOutputStream : public ZeroCopyOutputStream {
public:
// Creates a stream that writes to the given C++ ostream.
// If a block_size is given, it specifies the size of the buffers
// that should be returned by Next(). Otherwise, a reasonable default
// is used.
explicit OstreamOutputStream(ostream* stream, int block_size = -1);
~OstreamOutputStream();
// implements ZeroCopyOutputStream ---------------------------------
bool Next(void** data, int* size);
void BackUp(int count);
int64 ByteCount() const;
private:
class LIBPROTOBUF_EXPORT CopyingOstreamOutputStream : public CopyingOutputStream {
public:
CopyingOstreamOutputStream(ostream* output);
~CopyingOstreamOutputStream();
// implements CopyingOutputStream --------------------------------
bool Write(const void* buffer, int size);
private:
// The stream.
ostream* output_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingOstreamOutputStream);
};
CopyingOstreamOutputStream copying_output_;
CopyingOutputStreamAdaptor impl_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OstreamOutputStream);
};
// ===================================================================
// A ZeroCopyInputStream which reads from several other streams in sequence.
// ConcatenatingInputStream is unable to distinguish between end-of-stream
// and read errors in the underlying streams, so it assumes any errors mean
// end-of-stream. So, if the underlying streams fail for any other reason,
// ConcatenatingInputStream may do odd things. It is suggested that you do
// not use ConcatenatingInputStream on streams that might produce read errors
// other than end-of-stream.
class LIBPROTOBUF_EXPORT ConcatenatingInputStream : public ZeroCopyInputStream {
public:
// All streams passed in as well as the array itself must remain valid
// until the ConcatenatingInputStream is destroyed.
ConcatenatingInputStream(ZeroCopyInputStream* const streams[], int count);
~ConcatenatingInputStream();
// implements ZeroCopyInputStream ----------------------------------
bool Next(const void** data, int* size);
void BackUp(int count);
bool Skip(int count);
int64 ByteCount() const;
private:
// As streams are retired, streams_ is incremented and count_ is
// decremented.
ZeroCopyInputStream* const* streams_;
int stream_count_;
int64 bytes_retired_; // Bytes read from previous streams.
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ConcatenatingInputStream);
};
// ===================================================================
// A ZeroCopyInputStream which wraps some other stream and limits it to
// a particular byte count.
class LIBPROTOBUF_EXPORT LimitingInputStream : public ZeroCopyInputStream {
public:
LimitingInputStream(ZeroCopyInputStream* input, int64 limit);
~LimitingInputStream();
// implements ZeroCopyInputStream ----------------------------------
bool Next(const void** data, int* size);
void BackUp(int count);
bool Skip(int count);
int64 ByteCount() const;
private:
ZeroCopyInputStream* input_;
int64 limit_; // Decreases as we go, becomes negative if we overshoot.
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LimitingInputStream);
};
// ===================================================================
} // namespace io
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__

View file

@ -0,0 +1,393 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
namespace protobuf {
namespace io {
namespace {
// Default block size for Copying{In,Out}putStreamAdaptor.
static const int kDefaultBlockSize = 8192;
} // namespace
// ===================================================================
ArrayInputStream::ArrayInputStream(const void* data, int size,
int block_size)
: data_(reinterpret_cast<const uint8*>(data)),
size_(size),
block_size_(block_size > 0 ? block_size : size),
position_(0),
last_returned_size_(0) {
}
ArrayInputStream::~ArrayInputStream() {
}
bool ArrayInputStream::Next(const void** data, int* size) {
if (position_ < size_) {
last_returned_size_ = min(block_size_, size_ - position_);
*data = data_ + position_;
*size = last_returned_size_;
position_ += last_returned_size_;
return true;
} else {
// We're at the end of the array.
last_returned_size_ = 0; // Don't let caller back up.
return false;
}
}
void ArrayInputStream::BackUp(int count) {
GOOGLE_CHECK_GT(last_returned_size_, 0)
<< "BackUp() can only be called after a successful Next().";
GOOGLE_CHECK_LE(count, last_returned_size_);
GOOGLE_CHECK_GE(count, 0);
position_ -= count;
last_returned_size_ = 0; // Don't let caller back up further.
}
bool ArrayInputStream::Skip(int count) {
GOOGLE_CHECK_GE(count, 0);
last_returned_size_ = 0; // Don't let caller back up.
if (count > size_ - position_) {
position_ = size_;
return false;
} else {
position_ += count;
return true;
}
}
int64 ArrayInputStream::ByteCount() const {
return position_;
}
// ===================================================================
ArrayOutputStream::ArrayOutputStream(void* data, int size, int block_size)
: data_(reinterpret_cast<uint8*>(data)),
size_(size),
block_size_(block_size > 0 ? block_size : size),
position_(0),
last_returned_size_(0) {
}
ArrayOutputStream::~ArrayOutputStream() {
}
bool ArrayOutputStream::Next(void** data, int* size) {
if (position_ < size_) {
last_returned_size_ = min(block_size_, size_ - position_);
*data = data_ + position_;
*size = last_returned_size_;
position_ += last_returned_size_;
return true;
} else {
// We're at the end of the array.
last_returned_size_ = 0; // Don't let caller back up.
return false;
}
}
void ArrayOutputStream::BackUp(int count) {
GOOGLE_CHECK_GT(last_returned_size_, 0)
<< "BackUp() can only be called after a successful Next().";
GOOGLE_CHECK_LE(count, last_returned_size_);
GOOGLE_CHECK_GE(count, 0);
position_ -= count;
last_returned_size_ = 0; // Don't let caller back up further.
}
int64 ArrayOutputStream::ByteCount() const {
return position_;
}
// ===================================================================
StringOutputStream::StringOutputStream(string* target)
: target_(target) {
}
StringOutputStream::~StringOutputStream() {
}
bool StringOutputStream::Next(void** data, int* size) {
int old_size = target_->size();
// Grow the string.
if (old_size < target_->capacity()) {
// Resize the string to match its capacity, since we can get away
// without a memory allocation this way.
STLStringResizeUninitialized(target_, target_->capacity());
} else {
// Size has reached capacity, so double the size. Also make sure
// that the new size is at least kMinimumSize.
STLStringResizeUninitialized(
target_,
max(old_size * 2,
kMinimumSize + 0)); // "+ 0" works around GCC4 weirdness.
}
*data = string_as_array(target_) + old_size;
*size = target_->size() - old_size;
return true;
}
void StringOutputStream::BackUp(int count) {
GOOGLE_CHECK_GE(count, 0);
GOOGLE_CHECK_LE(count, target_->size());
target_->resize(target_->size() - count);
}
int64 StringOutputStream::ByteCount() const {
return target_->size();
}
// ===================================================================
CopyingInputStream::~CopyingInputStream() {}
int CopyingInputStream::Skip(int count) {
char junk[4096];
int skipped = 0;
while (skipped < count) {
int bytes = Read(junk, min(count - skipped,
implicit_cast<int>(sizeof(junk))));
if (bytes <= 0) {
// EOF or read error.
return skipped;
}
skipped += bytes;
}
return skipped;
}
CopyingInputStreamAdaptor::CopyingInputStreamAdaptor(
CopyingInputStream* copying_stream, int block_size)
: copying_stream_(copying_stream),
owns_copying_stream_(false),
failed_(false),
position_(0),
buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
buffer_used_(0),
backup_bytes_(0) {
}
CopyingInputStreamAdaptor::~CopyingInputStreamAdaptor() {
if (owns_copying_stream_) {
delete copying_stream_;
}
}
bool CopyingInputStreamAdaptor::Next(const void** data, int* size) {
if (failed_) {
// Already failed on a previous read.
return false;
}
AllocateBufferIfNeeded();
if (backup_bytes_ > 0) {
// We have data left over from a previous BackUp(), so just return that.
*data = buffer_.get() + buffer_used_ - backup_bytes_;
*size = backup_bytes_;
backup_bytes_ = 0;
return true;
}
// Read new data into the buffer.
buffer_used_ = copying_stream_->Read(buffer_.get(), buffer_size_);
if (buffer_used_ <= 0) {
// EOF or read error. We don't need the buffer anymore.
if (buffer_used_ < 0) {
// Read error (not EOF).
failed_ = true;
}
FreeBuffer();
return false;
}
position_ += buffer_used_;
*size = buffer_used_;
*data = buffer_.get();
return true;
}
void CopyingInputStreamAdaptor::BackUp(int count) {
GOOGLE_CHECK(backup_bytes_ == 0 && buffer_.get() != NULL)
<< " BackUp() can only be called after Next().";
GOOGLE_CHECK_LE(count, buffer_used_)
<< " Can't back up over more bytes than were returned by the last call"
" to Next().";
GOOGLE_CHECK_GE(count, 0)
<< " Parameter to BackUp() can't be negative.";
backup_bytes_ = count;
}
bool CopyingInputStreamAdaptor::Skip(int count) {
GOOGLE_CHECK_GE(count, 0);
if (failed_) {
// Already failed on a previous read.
return false;
}
// First skip any bytes left over from a previous BackUp().
if (backup_bytes_ >= count) {
// We have more data left over than we're trying to skip. Just chop it.
backup_bytes_ -= count;
return true;
}
count -= backup_bytes_;
backup_bytes_ = 0;
int skipped = copying_stream_->Skip(count);
position_ += skipped;
return skipped == count;
}
int64 CopyingInputStreamAdaptor::ByteCount() const {
return position_ - backup_bytes_;
}
void CopyingInputStreamAdaptor::AllocateBufferIfNeeded() {
if (buffer_.get() == NULL) {
buffer_.reset(new uint8[buffer_size_]);
}
}
void CopyingInputStreamAdaptor::FreeBuffer() {
GOOGLE_CHECK_EQ(backup_bytes_, 0);
buffer_used_ = 0;
buffer_.reset();
}
// ===================================================================
CopyingOutputStream::~CopyingOutputStream() {}
CopyingOutputStreamAdaptor::CopyingOutputStreamAdaptor(
CopyingOutputStream* copying_stream, int block_size)
: copying_stream_(copying_stream),
owns_copying_stream_(false),
failed_(false),
position_(0),
buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
buffer_used_(0) {
}
CopyingOutputStreamAdaptor::~CopyingOutputStreamAdaptor() {
WriteBuffer();
if (owns_copying_stream_) {
delete copying_stream_;
}
}
bool CopyingOutputStreamAdaptor::Flush() {
return WriteBuffer();
}
bool CopyingOutputStreamAdaptor::Next(void** data, int* size) {
if (buffer_used_ == buffer_size_) {
if (!WriteBuffer()) return false;
}
AllocateBufferIfNeeded();
*data = buffer_.get() + buffer_used_;
*size = buffer_size_ - buffer_used_;
buffer_used_ = buffer_size_;
return true;
}
void CopyingOutputStreamAdaptor::BackUp(int count) {
GOOGLE_CHECK_GE(count, 0);
GOOGLE_CHECK_EQ(buffer_used_, buffer_size_)
<< " BackUp() can only be called after Next().";
GOOGLE_CHECK_LE(count, buffer_used_)
<< " Can't back up over more bytes than were returned by the last call"
" to Next().";
buffer_used_ -= count;
}
int64 CopyingOutputStreamAdaptor::ByteCount() const {
return position_ + buffer_used_;
}
bool CopyingOutputStreamAdaptor::WriteBuffer() {
if (failed_) {
// Already failed on a previous write.
return false;
}
if (buffer_used_ == 0) return true;
if (copying_stream_->Write(buffer_.get(), buffer_used_)) {
position_ += buffer_used_;
buffer_used_ = 0;
return true;
} else {
failed_ = true;
FreeBuffer();
return false;
}
}
void CopyingOutputStreamAdaptor::AllocateBufferIfNeeded() {
if (buffer_ == NULL) {
buffer_.reset(new uint8[buffer_size_]);
}
}
void CopyingOutputStreamAdaptor::FreeBuffer() {
buffer_used_ = 0;
buffer_.reset();
}
// ===================================================================
} // namespace io
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,340 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This file contains common implementations of the interfaces defined in
// zero_copy_stream.h which are included in the "lite" protobuf library.
// These implementations cover I/O on raw arrays and strings, as well as
// adaptors which make it easy to implement streams based on traditional
// streams. Of course, many users will probably want to write their own
// implementations of these interfaces specific to the particular I/O
// abstractions they prefer to use, but these should cover the most common
// cases.
#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_LITE_H__
#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_LITE_H__
#include <string>
#include <iosfwd>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
namespace io {
// ===================================================================
// A ZeroCopyInputStream backed by an in-memory array of bytes.
class LIBPROTOBUF_EXPORT ArrayInputStream : public ZeroCopyInputStream {
public:
// Create an InputStream that returns the bytes pointed to by "data".
// "data" remains the property of the caller but must remain valid until
// the stream is destroyed. If a block_size is given, calls to Next()
// will return data blocks no larger than the given size. Otherwise, the
// first call to Next() returns the entire array. block_size is mainly
// useful for testing; in production you would probably never want to set
// it.
ArrayInputStream(const void* data, int size, int block_size = -1);
~ArrayInputStream();
// implements ZeroCopyInputStream ----------------------------------
bool Next(const void** data, int* size);
void BackUp(int count);
bool Skip(int count);
int64 ByteCount() const;
private:
const uint8* const data_; // The byte array.
const int size_; // Total size of the array.
const int block_size_; // How many bytes to return at a time.
int position_;
int last_returned_size_; // How many bytes we returned last time Next()
// was called (used for error checking only).
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayInputStream);
};
// ===================================================================
// A ZeroCopyOutputStream backed by an in-memory array of bytes.
class LIBPROTOBUF_EXPORT ArrayOutputStream : public ZeroCopyOutputStream {
public:
// Create an OutputStream that writes to the bytes pointed to by "data".
// "data" remains the property of the caller but must remain valid until
// the stream is destroyed. If a block_size is given, calls to Next()
// will return data blocks no larger than the given size. Otherwise, the
// first call to Next() returns the entire array. block_size is mainly
// useful for testing; in production you would probably never want to set
// it.
ArrayOutputStream(void* data, int size, int block_size = -1);
~ArrayOutputStream();
// implements ZeroCopyOutputStream ---------------------------------
bool Next(void** data, int* size);
void BackUp(int count);
int64 ByteCount() const;
private:
uint8* const data_; // The byte array.
const int size_; // Total size of the array.
const int block_size_; // How many bytes to return at a time.
int position_;
int last_returned_size_; // How many bytes we returned last time Next()
// was called (used for error checking only).
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayOutputStream);
};
// ===================================================================
// A ZeroCopyOutputStream which appends bytes to a string.
class LIBPROTOBUF_EXPORT StringOutputStream : public ZeroCopyOutputStream {
public:
// Create a StringOutputStream which appends bytes to the given string.
// The string remains property of the caller, but it MUST NOT be accessed
// in any way until the stream is destroyed.
//
// Hint: If you call target->reserve(n) before creating the stream,
// the first call to Next() will return at least n bytes of buffer
// space.
explicit StringOutputStream(string* target);
~StringOutputStream();
// implements ZeroCopyOutputStream ---------------------------------
bool Next(void** data, int* size);
void BackUp(int count);
int64 ByteCount() const;
private:
static const int kMinimumSize = 16;
string* target_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOutputStream);
};
// Note: There is no StringInputStream. Instead, just create an
// ArrayInputStream as follows:
// ArrayInputStream input(str.data(), str.size());
// ===================================================================
// A generic traditional input stream interface.
//
// Lots of traditional input streams (e.g. file descriptors, C stdio
// streams, and C++ iostreams) expose an interface where every read
// involves copying bytes into a buffer. If you want to take such an
// interface and make a ZeroCopyInputStream based on it, simply implement
// CopyingInputStream and then use CopyingInputStreamAdaptor.
//
// CopyingInputStream implementations should avoid buffering if possible.
// CopyingInputStreamAdaptor does its own buffering and will read data
// in large blocks.
class LIBPROTOBUF_EXPORT CopyingInputStream {
public:
virtual ~CopyingInputStream();
// Reads up to "size" bytes into the given buffer. Returns the number of
// bytes read. Read() waits until at least one byte is available, or
// returns zero if no bytes will ever become available (EOF), or -1 if a
// permanent read error occurred.
virtual int Read(void* buffer, int size) = 0;
// Skips the next "count" bytes of input. Returns the number of bytes
// actually skipped. This will always be exactly equal to "count" unless
// EOF was reached or a permanent read error occurred.
//
// The default implementation just repeatedly calls Read() into a scratch
// buffer.
virtual int Skip(int count);
};
// A ZeroCopyInputStream which reads from a CopyingInputStream. This is
// useful for implementing ZeroCopyInputStreams that read from traditional
// streams. Note that this class is not really zero-copy.
//
// If you want to read from file descriptors or C++ istreams, this is
// already implemented for you: use FileInputStream or IstreamInputStream
// respectively.
class LIBPROTOBUF_EXPORT CopyingInputStreamAdaptor : public ZeroCopyInputStream {
public:
// Creates a stream that reads from the given CopyingInputStream.
// If a block_size is given, it specifies the number of bytes that
// should be read and returned with each call to Next(). Otherwise,
// a reasonable default is used. The caller retains ownership of
// copying_stream unless SetOwnsCopyingStream(true) is called.
explicit CopyingInputStreamAdaptor(CopyingInputStream* copying_stream,
int block_size = -1);
~CopyingInputStreamAdaptor();
// Call SetOwnsCopyingStream(true) to tell the CopyingInputStreamAdaptor to
// delete the underlying CopyingInputStream when it is destroyed.
void SetOwnsCopyingStream(bool value) { owns_copying_stream_ = value; }
// implements ZeroCopyInputStream ----------------------------------
bool Next(const void** data, int* size);
void BackUp(int count);
bool Skip(int count);
int64 ByteCount() const;
private:
// Insures that buffer_ is not NULL.
void AllocateBufferIfNeeded();
// Frees the buffer and resets buffer_used_.
void FreeBuffer();
// The underlying copying stream.
CopyingInputStream* copying_stream_;
bool owns_copying_stream_;
// True if we have seen a permenant error from the underlying stream.
bool failed_;
// The current position of copying_stream_, relative to the point where
// we started reading.
int64 position_;
// Data is read into this buffer. It may be NULL if no buffer is currently
// in use. Otherwise, it points to an array of size buffer_size_.
scoped_array<uint8> buffer_;
const int buffer_size_;
// Number of valid bytes currently in the buffer (i.e. the size last
// returned by Next()). 0 <= buffer_used_ <= buffer_size_.
int buffer_used_;
// Number of bytes in the buffer which were backed up over by a call to
// BackUp(). These need to be returned again.
// 0 <= backup_bytes_ <= buffer_used_
int backup_bytes_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingInputStreamAdaptor);
};
// ===================================================================
// A generic traditional output stream interface.
//
// Lots of traditional output streams (e.g. file descriptors, C stdio
// streams, and C++ iostreams) expose an interface where every write
// involves copying bytes from a buffer. If you want to take such an
// interface and make a ZeroCopyOutputStream based on it, simply implement
// CopyingOutputStream and then use CopyingOutputStreamAdaptor.
//
// CopyingOutputStream implementations should avoid buffering if possible.
// CopyingOutputStreamAdaptor does its own buffering and will write data
// in large blocks.
class LIBPROTOBUF_EXPORT CopyingOutputStream {
public:
virtual ~CopyingOutputStream();
// Writes "size" bytes from the given buffer to the output. Returns true
// if successful, false on a write error.
virtual bool Write(const void* buffer, int size) = 0;
};
// A ZeroCopyOutputStream which writes to a CopyingOutputStream. This is
// useful for implementing ZeroCopyOutputStreams that write to traditional
// streams. Note that this class is not really zero-copy.
//
// If you want to write to file descriptors or C++ ostreams, this is
// already implemented for you: use FileOutputStream or OstreamOutputStream
// respectively.
class LIBPROTOBUF_EXPORT CopyingOutputStreamAdaptor : public ZeroCopyOutputStream {
public:
// Creates a stream that writes to the given Unix file descriptor.
// If a block_size is given, it specifies the size of the buffers
// that should be returned by Next(). Otherwise, a reasonable default
// is used.
explicit CopyingOutputStreamAdaptor(CopyingOutputStream* copying_stream,
int block_size = -1);
~CopyingOutputStreamAdaptor();
// Writes all pending data to the underlying stream. Returns false if a
// write error occurred on the underlying stream. (The underlying
// stream itself is not necessarily flushed.)
bool Flush();
// Call SetOwnsCopyingStream(true) to tell the CopyingOutputStreamAdaptor to
// delete the underlying CopyingOutputStream when it is destroyed.
void SetOwnsCopyingStream(bool value) { owns_copying_stream_ = value; }
// implements ZeroCopyOutputStream ---------------------------------
bool Next(void** data, int* size);
void BackUp(int count);
int64 ByteCount() const;
private:
// Write the current buffer, if it is present.
bool WriteBuffer();
// Insures that buffer_ is not NULL.
void AllocateBufferIfNeeded();
// Frees the buffer.
void FreeBuffer();
// The underlying copying stream.
CopyingOutputStream* copying_stream_;
bool owns_copying_stream_;
// True if we have seen a permenant error from the underlying stream.
bool failed_;
// The current position of copying_stream_, relative to the point where
// we started writing.
int64 position_;
// Data is written from this buffer. It may be NULL if no buffer is
// currently in use. Otherwise, it points to an array of size buffer_size_.
scoped_array<uint8> buffer_;
const int buffer_size_;
// Number of valid bytes currently in the buffer (i.e. the size last
// returned by Next()). When BackUp() is called, we just reduce this.
// 0 <= buffer_used_ <= buffer_size_.
int buffer_used_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingOutputStreamAdaptor);
};
// ===================================================================
} // namespace io
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_LITE_H__

View file

@ -0,0 +1,721 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Testing strategy: For each type of I/O (array, string, file, etc.) we
// create an output stream and write some data to it, then create a
// corresponding input stream to read the same data back and expect it to
// match. When the data is written, it is written in several small chunks
// of varying sizes, with a BackUp() after each chunk. It is read back
// similarly, but with chunks separated at different points. The whole
// process is run with a variety of block sizes for both the input and
// the output.
//
// TODO(kenton): Rewrite this test to bring it up to the standards of all
// the other proto2 tests. May want to wait for gTest to implement
// "parametized tests" so that one set of tests can be used on all the
// implementations.
#include "config.h"
#ifdef _MSC_VER
#include <io.h>
#else
#include <unistd.h>
#endif
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sstream>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#if HAVE_ZLIB
#include <google/protobuf/io/gzip_stream.h>
#endif
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/googletest.h>
#include <google/protobuf/testing/file.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace io {
namespace {
#ifdef _WIN32
#define pipe(fds) _pipe(fds, 4096, O_BINARY)
#endif
#ifndef O_BINARY
#ifdef _O_BINARY
#define O_BINARY _O_BINARY
#else
#define O_BINARY 0 // If this isn't defined, the platform doesn't need it.
#endif
#endif
class IoTest : public testing::Test {
protected:
// Test helpers.
// Helper to write an array of data to an output stream.
bool WriteToOutput(ZeroCopyOutputStream* output, const void* data, int size);
// Helper to read a fixed-length array of data from an input stream.
int ReadFromInput(ZeroCopyInputStream* input, void* data, int size);
// Write a string to the output stream.
void WriteString(ZeroCopyOutputStream* output, const string& str);
// Read a number of bytes equal to the size of the given string and checks
// that it matches the string.
void ReadString(ZeroCopyInputStream* input, const string& str);
// Writes some text to the output stream in a particular order. Returns
// the number of bytes written, incase the caller needs that to set up an
// input stream.
int WriteStuff(ZeroCopyOutputStream* output);
// Reads text from an input stream and expects it to match what
// WriteStuff() writes.
void ReadStuff(ZeroCopyInputStream* input);
// Similar to WriteStuff, but performs more sophisticated testing.
int WriteStuffLarge(ZeroCopyOutputStream* output);
// Reads and tests a stream that should have been written to
// via WriteStuffLarge().
void ReadStuffLarge(ZeroCopyInputStream* input);
#if HAVE_ZLIB
string Compress(const string& data, const GzipOutputStream::Options& options);
string Uncompress(const string& data);
#endif
static const int kBlockSizes[];
static const int kBlockSizeCount;
};
const int IoTest::kBlockSizes[] = {-1, 1, 2, 5, 7, 10, 23, 64};
const int IoTest::kBlockSizeCount = GOOGLE_ARRAYSIZE(IoTest::kBlockSizes);
bool IoTest::WriteToOutput(ZeroCopyOutputStream* output,
const void* data, int size) {
const uint8* in = reinterpret_cast<const uint8*>(data);
int in_size = size;
void* out;
int out_size;
while (true) {
if (!output->Next(&out, &out_size)) {
return false;
}
EXPECT_GT(out_size, 0);
if (in_size <= out_size) {
memcpy(out, in, in_size);
output->BackUp(out_size - in_size);
return true;
}
memcpy(out, in, out_size);
in += out_size;
in_size -= out_size;
}
}
#define MAX_REPEATED_ZEROS 100
int IoTest::ReadFromInput(ZeroCopyInputStream* input, void* data, int size) {
uint8* out = reinterpret_cast<uint8*>(data);
int out_size = size;
const void* in;
int in_size = 0;
int repeated_zeros = 0;
while (true) {
if (!input->Next(&in, &in_size)) {
return size - out_size;
}
EXPECT_GT(in_size, -1);
if (in_size == 0) {
repeated_zeros++;
} else {
repeated_zeros = 0;
}
EXPECT_LT(repeated_zeros, MAX_REPEATED_ZEROS);
if (out_size <= in_size) {
memcpy(out, in, out_size);
if (in_size > out_size) {
input->BackUp(in_size - out_size);
}
return size; // Copied all of it.
}
memcpy(out, in, in_size);
out += in_size;
out_size -= in_size;
}
}
void IoTest::WriteString(ZeroCopyOutputStream* output, const string& str) {
EXPECT_TRUE(WriteToOutput(output, str.c_str(), str.size()));
}
void IoTest::ReadString(ZeroCopyInputStream* input, const string& str) {
scoped_array<char> buffer(new char[str.size() + 1]);
buffer[str.size()] = '\0';
EXPECT_EQ(ReadFromInput(input, buffer.get(), str.size()), str.size());
EXPECT_STREQ(str.c_str(), buffer.get());
}
int IoTest::WriteStuff(ZeroCopyOutputStream* output) {
WriteString(output, "Hello world!\n");
WriteString(output, "Some te");
WriteString(output, "xt. Blah blah.");
WriteString(output, "abcdefg");
WriteString(output, "01234567890123456789");
WriteString(output, "foobar");
EXPECT_EQ(output->ByteCount(), 68);
int result = output->ByteCount();
return result;
}
// Reads text from an input stream and expects it to match what WriteStuff()
// writes.
void IoTest::ReadStuff(ZeroCopyInputStream* input) {
ReadString(input, "Hello world!\n");
ReadString(input, "Some text. ");
ReadString(input, "Blah ");
ReadString(input, "blah.");
ReadString(input, "abcdefg");
EXPECT_TRUE(input->Skip(20));
ReadString(input, "foo");
ReadString(input, "bar");
EXPECT_EQ(input->ByteCount(), 68);
uint8 byte;
EXPECT_EQ(ReadFromInput(input, &byte, 1), 0);
}
int IoTest::WriteStuffLarge(ZeroCopyOutputStream* output) {
WriteString(output, "Hello world!\n");
WriteString(output, "Some te");
WriteString(output, "xt. Blah blah.");
WriteString(output, string(100000, 'x')); // A very long string
WriteString(output, string(100000, 'y')); // A very long string
WriteString(output, "01234567890123456789");
EXPECT_EQ(output->ByteCount(), 200055);
int result = output->ByteCount();
return result;
}
// Reads text from an input stream and expects it to match what WriteStuff()
// writes.
void IoTest::ReadStuffLarge(ZeroCopyInputStream* input) {
ReadString(input, "Hello world!\nSome text. ");
EXPECT_TRUE(input->Skip(5));
ReadString(input, "blah.");
EXPECT_TRUE(input->Skip(100000 - 10));
ReadString(input, string(10, 'x') + string(100000 - 20000, 'y'));
EXPECT_TRUE(input->Skip(20000 - 10));
ReadString(input, "yyyyyyyyyy01234567890123456789");
EXPECT_EQ(input->ByteCount(), 200055);
uint8 byte;
EXPECT_EQ(ReadFromInput(input, &byte, 1), 0);
}
// ===================================================================
TEST_F(IoTest, ArrayIo) {
const int kBufferSize = 256;
uint8 buffer[kBufferSize];
for (int i = 0; i < kBlockSizeCount; i++) {
for (int j = 0; j < kBlockSizeCount; j++) {
int size;
{
ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
size = WriteStuff(&output);
}
{
ArrayInputStream input(buffer, size, kBlockSizes[j]);
ReadStuff(&input);
}
}
}
}
#if HAVE_ZLIB
TEST_F(IoTest, GzipIo) {
const int kBufferSize = 2*1024;
uint8* buffer = new uint8[kBufferSize];
for (int i = 0; i < kBlockSizeCount; i++) {
for (int j = 0; j < kBlockSizeCount; j++) {
for (int z = 0; z < kBlockSizeCount; z++) {
int gzip_buffer_size = kBlockSizes[z];
int size;
{
ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
GzipOutputStream gzout(
&output, GzipOutputStream::GZIP, gzip_buffer_size);
WriteStuff(&gzout);
gzout.Close();
size = output.ByteCount();
}
{
ArrayInputStream input(buffer, size, kBlockSizes[j]);
GzipInputStream gzin(
&input, GzipInputStream::GZIP, gzip_buffer_size);
ReadStuff(&gzin);
}
}
}
}
delete [] buffer;
}
TEST_F(IoTest, ZlibIo) {
const int kBufferSize = 2*1024;
uint8* buffer = new uint8[kBufferSize];
for (int i = 0; i < kBlockSizeCount; i++) {
for (int j = 0; j < kBlockSizeCount; j++) {
for (int z = 0; z < kBlockSizeCount; z++) {
int gzip_buffer_size = kBlockSizes[z];
int size;
{
ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
GzipOutputStream gzout(
&output, GzipOutputStream::ZLIB, gzip_buffer_size);
WriteStuff(&gzout);
gzout.Close();
size = output.ByteCount();
}
{
ArrayInputStream input(buffer, size, kBlockSizes[j]);
GzipInputStream gzin(
&input, GzipInputStream::ZLIB, gzip_buffer_size);
ReadStuff(&gzin);
}
}
}
}
delete [] buffer;
}
TEST_F(IoTest, ZlibIoInputAutodetect) {
const int kBufferSize = 2*1024;
uint8* buffer = new uint8[kBufferSize];
int size;
{
ArrayOutputStream output(buffer, kBufferSize);
GzipOutputStream gzout(&output, GzipOutputStream::ZLIB);
WriteStuff(&gzout);
gzout.Close();
size = output.ByteCount();
}
{
ArrayInputStream input(buffer, size);
GzipInputStream gzin(&input, GzipInputStream::AUTO);
ReadStuff(&gzin);
}
{
ArrayOutputStream output(buffer, kBufferSize);
GzipOutputStream gzout(&output, GzipOutputStream::GZIP);
WriteStuff(&gzout);
gzout.Close();
size = output.ByteCount();
}
{
ArrayInputStream input(buffer, size);
GzipInputStream gzin(&input, GzipInputStream::AUTO);
ReadStuff(&gzin);
}
delete [] buffer;
}
string IoTest::Compress(const string& data,
const GzipOutputStream::Options& options) {
string result;
{
StringOutputStream output(&result);
GzipOutputStream gzout(&output, options);
WriteToOutput(&gzout, data.data(), data.size());
}
return result;
}
string IoTest::Uncompress(const string& data) {
string result;
{
ArrayInputStream input(data.data(), data.size());
GzipInputStream gzin(&input);
const void* buffer;
int size;
while (gzin.Next(&buffer, &size)) {
result.append(reinterpret_cast<const char*>(buffer), size);
}
}
return result;
}
TEST_F(IoTest, CompressionOptions) {
// Some ad-hoc testing of compression options.
string golden;
File::ReadFileToStringOrDie(
TestSourceDir() + "/google/protobuf/testdata/golden_message",
&golden);
GzipOutputStream::Options options;
string gzip_compressed = Compress(golden, options);
options.compression_level = 0;
string not_compressed = Compress(golden, options);
// Try zlib compression for fun.
options = GzipOutputStream::Options();
options.format = GzipOutputStream::ZLIB;
string zlib_compressed = Compress(golden, options);
// Uncompressed should be bigger than the original since it should have some
// sort of header.
EXPECT_GT(not_compressed.size(), golden.size());
// Higher compression levels should result in smaller sizes.
EXPECT_LT(zlib_compressed.size(), not_compressed.size());
// ZLIB format should differ from GZIP format.
EXPECT_TRUE(zlib_compressed != gzip_compressed);
// Everything should decompress correctly.
EXPECT_TRUE(Uncompress(not_compressed) == golden);
EXPECT_TRUE(Uncompress(gzip_compressed) == golden);
EXPECT_TRUE(Uncompress(zlib_compressed) == golden);
}
#endif
// There is no string input, only string output. Also, it doesn't support
// explicit block sizes. So, we'll only run one test and we'll use
// ArrayInput to read back the results.
TEST_F(IoTest, StringIo) {
string str;
{
StringOutputStream output(&str);
WriteStuff(&output);
}
{
ArrayInputStream input(str.data(), str.size());
ReadStuff(&input);
}
}
// To test files, we create a temporary file, write, read, truncate, repeat.
TEST_F(IoTest, FileIo) {
string filename = TestTempDir() + "/zero_copy_stream_test_file";
for (int i = 0; i < kBlockSizeCount; i++) {
for (int j = 0; j < kBlockSizeCount; j++) {
// Make a temporary file.
int file =
open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
ASSERT_GE(file, 0);
{
FileOutputStream output(file, kBlockSizes[i]);
WriteStuff(&output);
EXPECT_EQ(0, output.GetErrno());
}
// Rewind.
ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
{
FileInputStream input(file, kBlockSizes[j]);
ReadStuff(&input);
EXPECT_EQ(0, input.GetErrno());
}
close(file);
}
}
}
#if HAVE_ZLIB
TEST_F(IoTest, GzipFileIo) {
string filename = TestTempDir() + "/zero_copy_stream_test_file";
for (int i = 0; i < kBlockSizeCount; i++) {
for (int j = 0; j < kBlockSizeCount; j++) {
// Make a temporary file.
int file =
open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
ASSERT_GE(file, 0);
{
FileOutputStream output(file, kBlockSizes[i]);
GzipOutputStream gzout(&output);
WriteStuffLarge(&gzout);
gzout.Close();
output.Flush();
EXPECT_EQ(0, output.GetErrno());
}
// Rewind.
ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
{
FileInputStream input(file, kBlockSizes[j]);
GzipInputStream gzin(&input);
ReadStuffLarge(&gzin);
EXPECT_EQ(0, input.GetErrno());
}
close(file);
}
}
}
#endif
// MSVC raises various debugging exceptions if we try to use a file
// descriptor of -1, defeating our tests below. This class will disable
// these debug assertions while in scope.
class MsvcDebugDisabler {
public:
#if defined(_MSC_VER) && _MSC_VER >= 1400
MsvcDebugDisabler() {
old_handler_ = _set_invalid_parameter_handler(MyHandler);
old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0);
}
~MsvcDebugDisabler() {
old_handler_ = _set_invalid_parameter_handler(old_handler_);
old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_);
}
static void MyHandler(const wchar_t *expr,
const wchar_t *func,
const wchar_t *file,
unsigned int line,
uintptr_t pReserved) {
// do nothing
}
_invalid_parameter_handler old_handler_;
int old_mode_;
#else
// Dummy constructor and destructor to ensure that GCC doesn't complain
// that debug_disabler is an unused variable.
MsvcDebugDisabler() {}
~MsvcDebugDisabler() {}
#endif
};
// Test that FileInputStreams report errors correctly.
TEST_F(IoTest, FileReadError) {
MsvcDebugDisabler debug_disabler;
// -1 = invalid file descriptor.
FileInputStream input(-1);
const void* buffer;
int size;
EXPECT_FALSE(input.Next(&buffer, &size));
EXPECT_EQ(EBADF, input.GetErrno());
}
// Test that FileOutputStreams report errors correctly.
TEST_F(IoTest, FileWriteError) {
MsvcDebugDisabler debug_disabler;
// -1 = invalid file descriptor.
FileOutputStream input(-1);
void* buffer;
int size;
// The first call to Next() succeeds because it doesn't have anything to
// write yet.
EXPECT_TRUE(input.Next(&buffer, &size));
// Second call fails.
EXPECT_FALSE(input.Next(&buffer, &size));
EXPECT_EQ(EBADF, input.GetErrno());
}
// Pipes are not seekable, so File{Input,Output}Stream ends up doing some
// different things to handle them. We'll test by writing to a pipe and
// reading back from it.
TEST_F(IoTest, PipeIo) {
int files[2];
for (int i = 0; i < kBlockSizeCount; i++) {
for (int j = 0; j < kBlockSizeCount; j++) {
// Need to create a new pipe each time because ReadStuff() expects
// to see EOF at the end.
ASSERT_EQ(pipe(files), 0);
{
FileOutputStream output(files[1], kBlockSizes[i]);
WriteStuff(&output);
EXPECT_EQ(0, output.GetErrno());
}
close(files[1]); // Send EOF.
{
FileInputStream input(files[0], kBlockSizes[j]);
ReadStuff(&input);
EXPECT_EQ(0, input.GetErrno());
}
close(files[0]);
}
}
}
// Test using C++ iostreams.
TEST_F(IoTest, IostreamIo) {
for (int i = 0; i < kBlockSizeCount; i++) {
for (int j = 0; j < kBlockSizeCount; j++) {
{
stringstream stream;
{
OstreamOutputStream output(&stream, kBlockSizes[i]);
WriteStuff(&output);
EXPECT_FALSE(stream.fail());
}
{
IstreamInputStream input(&stream, kBlockSizes[j]);
ReadStuff(&input);
EXPECT_TRUE(stream.eof());
}
}
{
stringstream stream;
{
OstreamOutputStream output(&stream, kBlockSizes[i]);
WriteStuffLarge(&output);
EXPECT_FALSE(stream.fail());
}
{
IstreamInputStream input(&stream, kBlockSizes[j]);
ReadStuffLarge(&input);
EXPECT_TRUE(stream.eof());
}
}
}
}
}
// To test ConcatenatingInputStream, we create several ArrayInputStreams
// covering a buffer and then concatenate them.
TEST_F(IoTest, ConcatenatingInputStream) {
const int kBufferSize = 256;
uint8 buffer[kBufferSize];
// Fill the buffer.
ArrayOutputStream output(buffer, kBufferSize);
WriteStuff(&output);
// Now split it up into multiple streams of varying sizes.
ASSERT_EQ(68, output.ByteCount()); // Test depends on this.
ArrayInputStream input1(buffer , 12);
ArrayInputStream input2(buffer + 12, 7);
ArrayInputStream input3(buffer + 19, 6);
ArrayInputStream input4(buffer + 25, 15);
ArrayInputStream input5(buffer + 40, 0);
// Note: We want to make sure we have a stream boundary somewhere between
// bytes 42 and 62, which is the range that it Skip()ed by ReadStuff(). This
// tests that a bug that existed in the original code for Skip() is fixed.
ArrayInputStream input6(buffer + 40, 10);
ArrayInputStream input7(buffer + 50, 18); // Total = 68 bytes.
ZeroCopyInputStream* streams[] =
{&input1, &input2, &input3, &input4, &input5, &input6, &input7};
// Create the concatenating stream and read.
ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams));
ReadStuff(&input);
}
// To test LimitingInputStream, we write our golden text to a buffer, then
// create an ArrayInputStream that contains the whole buffer (not just the
// bytes written), then use a LimitingInputStream to limit it just to the
// bytes written.
TEST_F(IoTest, LimitingInputStream) {
const int kBufferSize = 256;
uint8 buffer[kBufferSize];
// Fill the buffer.
ArrayOutputStream output(buffer, kBufferSize);
WriteStuff(&output);
// Set up input.
ArrayInputStream array_input(buffer, kBufferSize);
LimitingInputStream input(&array_input, output.ByteCount());
ReadStuff(&input);
}
// Check that a zero-size array doesn't confuse the code.
TEST(ZeroSizeArray, Input) {
ArrayInputStream input(NULL, 0);
const void* data;
int size;
EXPECT_FALSE(input.Next(&data, &size));
}
TEST(ZeroSizeArray, Output) {
ArrayOutputStream output(NULL, 0);
void* data;
int size;
EXPECT_FALSE(output.Next(&data, &size));
}
} // namespace
} // namespace io
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,112 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
#include <string>
#include <iostream>
#include <google/protobuf/test_util_lite.h>
#include <google/protobuf/stubs/common.h>
using namespace std;
int main(int argc, char* argv[]) {
string data, packed_data;
{
protobuf_unittest::TestAllTypesLite message, message2, message3;
google::protobuf::TestUtilLite::ExpectClear(message);
google::protobuf::TestUtilLite::SetAllFields(&message);
message2.CopyFrom(message);
data = message.SerializeAsString();
message3.ParseFromString(data);
google::protobuf::TestUtilLite::ExpectAllFieldsSet(message);
google::protobuf::TestUtilLite::ExpectAllFieldsSet(message2);
google::protobuf::TestUtilLite::ExpectAllFieldsSet(message3);
google::protobuf::TestUtilLite::ModifyRepeatedFields(&message);
google::protobuf::TestUtilLite::ExpectRepeatedFieldsModified(message);
message.Clear();
google::protobuf::TestUtilLite::ExpectClear(message);
}
{
protobuf_unittest::TestAllExtensionsLite message, message2, message3;
google::protobuf::TestUtilLite::ExpectExtensionsClear(message);
google::protobuf::TestUtilLite::SetAllExtensions(&message);
message2.CopyFrom(message);
string extensions_data = message.SerializeAsString();
GOOGLE_CHECK(extensions_data == data);
message3.ParseFromString(extensions_data);
google::protobuf::TestUtilLite::ExpectAllExtensionsSet(message);
google::protobuf::TestUtilLite::ExpectAllExtensionsSet(message2);
google::protobuf::TestUtilLite::ExpectAllExtensionsSet(message3);
google::protobuf::TestUtilLite::ModifyRepeatedExtensions(&message);
google::protobuf::TestUtilLite::ExpectRepeatedExtensionsModified(message);
message.Clear();
google::protobuf::TestUtilLite::ExpectExtensionsClear(message);
}
{
protobuf_unittest::TestPackedTypesLite message, message2, message3;
google::protobuf::TestUtilLite::ExpectPackedClear(message);
google::protobuf::TestUtilLite::SetPackedFields(&message);
message2.CopyFrom(message);
packed_data = message.SerializeAsString();
message3.ParseFromString(packed_data);
google::protobuf::TestUtilLite::ExpectPackedFieldsSet(message);
google::protobuf::TestUtilLite::ExpectPackedFieldsSet(message2);
google::protobuf::TestUtilLite::ExpectPackedFieldsSet(message3);
google::protobuf::TestUtilLite::ModifyPackedFields(&message);
google::protobuf::TestUtilLite::ExpectPackedFieldsModified(message);
message.Clear();
google::protobuf::TestUtilLite::ExpectPackedClear(message);
}
{
protobuf_unittest::TestPackedExtensionsLite message, message2, message3;
google::protobuf::TestUtilLite::ExpectPackedExtensionsClear(message);
google::protobuf::TestUtilLite::SetPackedExtensions(&message);
message2.CopyFrom(message);
string packed_extensions_data = message.SerializeAsString();
GOOGLE_CHECK(packed_extensions_data == packed_data);
message3.ParseFromString(packed_extensions_data);
google::protobuf::TestUtilLite::ExpectPackedExtensionsSet(message);
google::protobuf::TestUtilLite::ExpectPackedExtensionsSet(message2);
google::protobuf::TestUtilLite::ExpectPackedExtensionsSet(message3);
google::protobuf::TestUtilLite::ModifyPackedExtensions(&message);
google::protobuf::TestUtilLite::ExpectPackedExtensionsModified(message);
message.Clear();
google::protobuf::TestUtilLite::ExpectPackedExtensionsClear(message);
}
cout << "PASS" << endl;
return 0;
}

View file

@ -0,0 +1,318 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <stack>
#include <google/protobuf/stubs/hash.h>
#include <google/protobuf/message.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/reflection_ops.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/map-util.h>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
namespace protobuf {
using internal::WireFormat;
using internal::ReflectionOps;
Message::~Message() {}
void Message::MergeFrom(const Message& from) {
const Descriptor* descriptor = GetDescriptor();
GOOGLE_CHECK_EQ(from.GetDescriptor(), descriptor)
<< ": Tried to merge from a message with a different type. "
"to: " << descriptor->full_name() << ", "
"from:" << from.GetDescriptor()->full_name();
ReflectionOps::Merge(from, this);
}
void Message::CheckTypeAndMergeFrom(const MessageLite& other) {
MergeFrom(*down_cast<const Message*>(&other));
}
void Message::CopyFrom(const Message& from) {
const Descriptor* descriptor = GetDescriptor();
GOOGLE_CHECK_EQ(from.GetDescriptor(), descriptor)
<< ": Tried to copy from a message with a different type."
"to: " << descriptor->full_name() << ", "
"from:" << from.GetDescriptor()->full_name();
ReflectionOps::Copy(from, this);
}
string Message::GetTypeName() const {
return GetDescriptor()->full_name();
}
void Message::Clear() {
ReflectionOps::Clear(this);
}
bool Message::IsInitialized() const {
return ReflectionOps::IsInitialized(*this);
}
void Message::FindInitializationErrors(vector<string>* errors) const {
return ReflectionOps::FindInitializationErrors(*this, "", errors);
}
string Message::InitializationErrorString() const {
vector<string> errors;
FindInitializationErrors(&errors);
return JoinStrings(errors, ", ");
}
void Message::CheckInitialized() const {
GOOGLE_CHECK(IsInitialized())
<< "Message of type \"" << GetDescriptor()->full_name()
<< "\" is missing required fields: " << InitializationErrorString();
}
void Message::DiscardUnknownFields() {
return ReflectionOps::DiscardUnknownFields(this);
}
bool Message::MergePartialFromCodedStream(io::CodedInputStream* input) {
return WireFormat::ParseAndMergePartial(input, this);
}
bool Message::ParseFromFileDescriptor(int file_descriptor) {
io::FileInputStream input(file_descriptor);
return ParseFromZeroCopyStream(&input) && input.GetErrno() == 0;
}
bool Message::ParsePartialFromFileDescriptor(int file_descriptor) {
io::FileInputStream input(file_descriptor);
return ParsePartialFromZeroCopyStream(&input) && input.GetErrno() == 0;
}
bool Message::ParseFromIstream(istream* input) {
io::IstreamInputStream zero_copy_input(input);
return ParseFromZeroCopyStream(&zero_copy_input) && input->eof();
}
bool Message::ParsePartialFromIstream(istream* input) {
io::IstreamInputStream zero_copy_input(input);
return ParsePartialFromZeroCopyStream(&zero_copy_input) && input->eof();
}
void Message::SerializeWithCachedSizes(
io::CodedOutputStream* output) const {
WireFormat::SerializeWithCachedSizes(*this, GetCachedSize(), output);
}
int Message::ByteSize() const {
int size = WireFormat::ByteSize(*this);
SetCachedSize(size);
return size;
}
void Message::SetCachedSize(int size) const {
GOOGLE_LOG(FATAL) << "Message class \"" << GetDescriptor()->full_name()
<< "\" implements neither SetCachedSize() nor ByteSize(). "
"Must implement one or the other.";
}
int Message::SpaceUsed() const {
return GetReflection()->SpaceUsed(*this);
}
bool Message::SerializeToFileDescriptor(int file_descriptor) const {
io::FileOutputStream output(file_descriptor);
return SerializeToZeroCopyStream(&output);
}
bool Message::SerializePartialToFileDescriptor(int file_descriptor) const {
io::FileOutputStream output(file_descriptor);
return SerializePartialToZeroCopyStream(&output);
}
bool Message::SerializeToOstream(ostream* output) const {
{
io::OstreamOutputStream zero_copy_output(output);
if (!SerializeToZeroCopyStream(&zero_copy_output)) return false;
}
return output->good();
}
bool Message::SerializePartialToOstream(ostream* output) const {
io::OstreamOutputStream zero_copy_output(output);
return SerializePartialToZeroCopyStream(&zero_copy_output);
}
Reflection::~Reflection() {}
// ===================================================================
// MessageFactory
MessageFactory::~MessageFactory() {}
namespace {
class GeneratedMessageFactory : public MessageFactory {
public:
GeneratedMessageFactory();
~GeneratedMessageFactory();
static GeneratedMessageFactory* singleton();
typedef void RegistrationFunc(const string&);
void RegisterFile(const char* file, RegistrationFunc* registration_func);
void RegisterType(const Descriptor* descriptor, const Message* prototype);
// implements MessageFactory ---------------------------------------
const Message* GetPrototype(const Descriptor* type);
private:
// Only written at static init time, so does not require locking.
hash_map<const char*, RegistrationFunc*,
hash<const char*>, streq> file_map_;
// Initialized lazily, so requires locking.
Mutex mutex_;
hash_map<const Descriptor*, const Message*> type_map_;
};
GeneratedMessageFactory* generated_message_factory_ = NULL;
GOOGLE_PROTOBUF_DECLARE_ONCE(generated_message_factory_once_init_);
void ShutdownGeneratedMessageFactory() {
delete generated_message_factory_;
}
void InitGeneratedMessageFactory() {
generated_message_factory_ = new GeneratedMessageFactory;
internal::OnShutdown(&ShutdownGeneratedMessageFactory);
}
GeneratedMessageFactory::GeneratedMessageFactory() {}
GeneratedMessageFactory::~GeneratedMessageFactory() {}
GeneratedMessageFactory* GeneratedMessageFactory::singleton() {
::google::protobuf::GoogleOnceInit(&generated_message_factory_once_init_,
&InitGeneratedMessageFactory);
return generated_message_factory_;
}
void GeneratedMessageFactory::RegisterFile(
const char* file, RegistrationFunc* registration_func) {
if (!InsertIfNotPresent(&file_map_, file, registration_func)) {
GOOGLE_LOG(FATAL) << "File is already registered: " << file;
}
}
void GeneratedMessageFactory::RegisterType(const Descriptor* descriptor,
const Message* prototype) {
GOOGLE_DCHECK_EQ(descriptor->file()->pool(), DescriptorPool::generated_pool())
<< "Tried to register a non-generated type with the generated "
"type registry.";
// This should only be called as a result of calling a file registration
// function during GetPrototype(), in which case we already have locked
// the mutex.
mutex_.AssertHeld();
if (!InsertIfNotPresent(&type_map_, descriptor, prototype)) {
GOOGLE_LOG(DFATAL) << "Type is already registered: " << descriptor->full_name();
}
}
const Message* GeneratedMessageFactory::GetPrototype(const Descriptor* type) {
{
ReaderMutexLock lock(&mutex_);
const Message* result = FindPtrOrNull(type_map_, type);
if (result != NULL) return result;
}
// If the type is not in the generated pool, then we can't possibly handle
// it.
if (type->file()->pool() != DescriptorPool::generated_pool()) return NULL;
// Apparently the file hasn't been registered yet. Let's do that now.
RegistrationFunc* registration_func =
FindPtrOrNull(file_map_, type->file()->name().c_str());
if (registration_func == NULL) {
GOOGLE_LOG(DFATAL) << "File appears to be in generated pool but wasn't "
"registered: " << type->file()->name();
return NULL;
}
WriterMutexLock lock(&mutex_);
// Check if another thread preempted us.
const Message* result = FindPtrOrNull(type_map_, type);
if (result == NULL) {
// Nope. OK, register everything.
registration_func(type->file()->name());
// Should be here now.
result = FindPtrOrNull(type_map_, type);
}
if (result == NULL) {
GOOGLE_LOG(DFATAL) << "Type appears to be in generated pool but wasn't "
<< "registered: " << type->full_name();
}
return result;
}
} // namespace
MessageFactory* MessageFactory::generated_factory() {
return GeneratedMessageFactory::singleton();
}
void MessageFactory::InternalRegisterGeneratedFile(
const char* filename, void (*register_messages)(const string&)) {
GeneratedMessageFactory::singleton()->RegisterFile(filename,
register_messages);
}
void MessageFactory::InternalRegisterGeneratedMessage(
const Descriptor* descriptor, const Message* prototype) {
GeneratedMessageFactory::singleton()->RegisterType(descriptor, prototype);
}
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,710 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Defines Message, the abstract interface implemented by non-lite
// protocol message objects. Although it's possible to implement this
// interface manually, most users will use the protocol compiler to
// generate implementations.
//
// Example usage:
//
// Say you have a message defined as:
//
// message Foo {
// optional string text = 1;
// repeated int32 numbers = 2;
// }
//
// Then, if you used the protocol compiler to generate a class from the above
// definition, you could use it like so:
//
// string data; // Will store a serialized version of the message.
//
// {
// // Create a message and serialize it.
// Foo foo;
// foo.set_text("Hello World!");
// foo.add_numbers(1);
// foo.add_numbers(5);
// foo.add_numbers(42);
//
// foo.SerializeToString(&data);
// }
//
// {
// // Parse the serialized message and check that it contains the
// // correct data.
// Foo foo;
// foo.ParseFromString(data);
//
// assert(foo.text() == "Hello World!");
// assert(foo.numbers_size() == 3);
// assert(foo.numbers(0) == 1);
// assert(foo.numbers(1) == 5);
// assert(foo.numbers(2) == 42);
// }
//
// {
// // Same as the last block, but do it dynamically via the Message
// // reflection interface.
// Message* foo = new Foo;
// Descriptor* descriptor = foo->GetDescriptor();
//
// // Get the descriptors for the fields we're interested in and verify
// // their types.
// FieldDescriptor* text_field = descriptor->FindFieldByName("text");
// assert(text_field != NULL);
// assert(text_field->type() == FieldDescriptor::TYPE_STRING);
// assert(text_field->label() == FieldDescriptor::TYPE_OPTIONAL);
// FieldDescriptor* numbers_field = descriptor->FindFieldByName("numbers");
// assert(numbers_field != NULL);
// assert(numbers_field->type() == FieldDescriptor::TYPE_INT32);
// assert(numbers_field->label() == FieldDescriptor::TYPE_REPEATED);
//
// // Parse the message.
// foo->ParseFromString(data);
//
// // Use the reflection interface to examine the contents.
// const Reflection* reflection = foo->GetReflection();
// assert(reflection->GetString(foo, text_field) == "Hello World!");
// assert(reflection->FieldSize(foo, numbers_field) == 3);
// assert(reflection->GetRepeatedInt32(foo, numbers_field, 0) == 1);
// assert(reflection->GetRepeatedInt32(foo, numbers_field, 1) == 5);
// assert(reflection->GetRepeatedInt32(foo, numbers_field, 2) == 42);
//
// delete foo;
// }
#ifndef GOOGLE_PROTOBUF_MESSAGE_H__
#define GOOGLE_PROTOBUF_MESSAGE_H__
#include <vector>
#include <string>
#ifdef __DECCXX
// HP C++'s iosfwd doesn't work.
#include <iostream>
#else
#include <iosfwd>
#endif
#include <google/protobuf/message_lite.h>
#include <google/protobuf/stubs/common.h>
#if defined(_WIN32) && defined(GetMessage)
// windows.h defines GetMessage() as a macro. Let's re-define it as an inline
// function. This is necessary because Reflection has a method called
// GetMessage() which we don't want overridden. The inline function should be
// equivalent for C++ users.
inline BOOL GetMessage_Win32(
LPMSG lpMsg, HWND hWnd,
UINT wMsgFilterMin, UINT wMsgFilterMax) {
return GetMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
}
#undef GetMessage
inline BOOL GetMessage(
LPMSG lpMsg, HWND hWnd,
UINT wMsgFilterMin, UINT wMsgFilterMax) {
return GetMessage_Win32(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
}
#endif
namespace google {
namespace protobuf {
// Defined in this file.
class Message;
class Reflection;
class MessageFactory;
// Defined in other files.
class Descriptor; // descriptor.h
class FieldDescriptor; // descriptor.h
class EnumDescriptor; // descriptor.h
class EnumValueDescriptor; // descriptor.h
namespace io {
class ZeroCopyInputStream; // zero_copy_stream.h
class ZeroCopyOutputStream; // zero_copy_stream.h
class CodedInputStream; // coded_stream.h
class CodedOutputStream; // coded_stream.h
}
class UnknownFieldSet; // unknown_field_set.h
// A container to hold message metadata.
struct Metadata {
const Descriptor* descriptor;
const Reflection* reflection;
};
// Returns the EnumDescriptor for enum type E, which must be a
// proto-declared enum type. Code generated by the protocol compiler
// will include specializations of this template for each enum type declared.
template <typename E>
const EnumDescriptor* GetEnumDescriptor();
// Abstract interface for protocol messages.
//
// See also MessageLite, which contains most every-day operations. Message
// adds descriptors and reflection on top of that.
//
// The methods of this class that are virtual but not pure-virtual have
// default implementations based on reflection. Message classes which are
// optimized for speed will want to override these with faster implementations,
// but classes optimized for code size may be happy with keeping them. See
// the optimize_for option in descriptor.proto.
class LIBPROTOBUF_EXPORT Message : public MessageLite {
public:
inline Message() {}
virtual ~Message();
// Basic Operations ------------------------------------------------
// Construct a new instance of the same type. Ownership is passed to the
// caller. (This is also defined in MessageLite, but is defined again here
// for return-type covariance.)
virtual Message* New() const = 0;
// Make this message into a copy of the given message. The given message
// must have the same descriptor, but need not necessarily be the same class.
// By default this is just implemented as "Clear(); MergeFrom(from);".
virtual void CopyFrom(const Message& from);
// Merge the fields from the given message into this message. Singular
// fields will be overwritten, except for embedded messages which will
// be merged. Repeated fields will be concatenated. The given message
// must be of the same type as this message (i.e. the exact same class).
virtual void MergeFrom(const Message& from);
// Verifies that IsInitialized() returns true. GOOGLE_CHECK-fails otherwise, with
// a nice error message.
void CheckInitialized() const;
// Slowly build a list of all required fields that are not set.
// This is much, much slower than IsInitialized() as it is implemented
// purely via reflection. Generally, you should not call this unless you
// have already determined that an error exists by calling IsInitialized().
void FindInitializationErrors(vector<string>* errors) const;
// Like FindInitializationErrors, but joins all the strings, delimited by
// commas, and returns them.
string InitializationErrorString() const;
// Clears all unknown fields from this message and all embedded messages.
// Normally, if unknown tag numbers are encountered when parsing a message,
// the tag and value are stored in the message's UnknownFieldSet and
// then written back out when the message is serialized. This allows servers
// which simply route messages to other servers to pass through messages
// that have new field definitions which they don't yet know about. However,
// this behavior can have security implications. To avoid it, call this
// method after parsing.
//
// See Reflection::GetUnknownFields() for more on unknown fields.
virtual void DiscardUnknownFields();
// Computes (an estimate of) the total number of bytes currently used for
// storing the message in memory. The default implementation calls the
// Reflection object's SpaceUsed() method.
virtual int SpaceUsed() const;
// Debugging & Testing----------------------------------------------
// Generates a human readable form of this message, useful for debugging
// and other purposes.
string DebugString() const;
// Like DebugString(), but with less whitespace.
string ShortDebugString() const;
// Like DebugString(), but do not escape UTF-8 byte sequences.
string Utf8DebugString() const;
// Convenience function useful in GDB. Prints DebugString() to stdout.
void PrintDebugString() const;
// Heavy I/O -------------------------------------------------------
// Additional parsing and serialization methods not implemented by
// MessageLite because they are not supported by the lite library.
// Parse a protocol buffer from a file descriptor. If successful, the entire
// input will be consumed.
bool ParseFromFileDescriptor(int file_descriptor);
// Like ParseFromFileDescriptor(), but accepts messages that are missing
// required fields.
bool ParsePartialFromFileDescriptor(int file_descriptor);
// Parse a protocol buffer from a C++ istream. If successful, the entire
// input will be consumed.
bool ParseFromIstream(istream* input);
// Like ParseFromIstream(), but accepts messages that are missing
// required fields.
bool ParsePartialFromIstream(istream* input);
// Serialize the message and write it to the given file descriptor. All
// required fields must be set.
bool SerializeToFileDescriptor(int file_descriptor) const;
// Like SerializeToFileDescriptor(), but allows missing required fields.
bool SerializePartialToFileDescriptor(int file_descriptor) const;
// Serialize the message and write it to the given C++ ostream. All
// required fields must be set.
bool SerializeToOstream(ostream* output) const;
// Like SerializeToOstream(), but allows missing required fields.
bool SerializePartialToOstream(ostream* output) const;
// Reflection-based methods ----------------------------------------
// These methods are pure-virtual in MessageLite, but Message provides
// reflection-based default implementations.
virtual string GetTypeName() const;
virtual void Clear();
virtual bool IsInitialized() const;
virtual void CheckTypeAndMergeFrom(const MessageLite& other);
virtual bool MergePartialFromCodedStream(io::CodedInputStream* input);
virtual int ByteSize() const;
virtual void SerializeWithCachedSizes(io::CodedOutputStream* output) const;
private:
// This is called only by the default implementation of ByteSize(), to
// update the cached size. If you override ByteSize(), you do not need
// to override this. If you do not override ByteSize(), you MUST override
// this; the default implementation will crash.
//
// The method is private because subclasses should never call it; only
// override it. Yes, C++ lets you do that. Crazy, huh?
virtual void SetCachedSize(int size) const;
public:
// Introspection ---------------------------------------------------
// Typedef for backwards-compatibility.
typedef google::protobuf::Reflection Reflection;
// Get a Descriptor for this message's type. This describes what
// fields the message contains, the types of those fields, etc.
const Descriptor* GetDescriptor() const { return GetMetadata().descriptor; }
// Get the Reflection interface for this Message, which can be used to
// read and modify the fields of the Message dynamically (in other words,
// without knowing the message type at compile time). This object remains
// property of the Message.
//
// This method remains virtual in case a subclass does not implement
// reflection and wants to override the default behavior.
virtual const Reflection* GetReflection() const {
return GetMetadata().reflection;
}
protected:
// Get a struct containing the metadata for the Message. Most subclasses only
// need to implement this method, rather than the GetDescriptor() and
// GetReflection() wrappers.
virtual Metadata GetMetadata() const = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Message);
};
// This interface contains methods that can be used to dynamically access
// and modify the fields of a protocol message. Their semantics are
// similar to the accessors the protocol compiler generates.
//
// To get the Reflection for a given Message, call Message::GetReflection().
//
// This interface is separate from Message only for efficiency reasons;
// the vast majority of implementations of Message will share the same
// implementation of Reflection (GeneratedMessageReflection,
// defined in generated_message.h), and all Messages of a particular class
// should share the same Reflection object (though you should not rely on
// the latter fact).
//
// There are several ways that these methods can be used incorrectly. For
// example, any of the following conditions will lead to undefined
// results (probably assertion failures):
// - The FieldDescriptor is not a field of this message type.
// - The method called is not appropriate for the field's type. For
// each field type in FieldDescriptor::TYPE_*, there is only one
// Get*() method, one Set*() method, and one Add*() method that is
// valid for that type. It should be obvious which (except maybe
// for TYPE_BYTES, which are represented using strings in C++).
// - A Get*() or Set*() method for singular fields is called on a repeated
// field.
// - GetRepeated*(), SetRepeated*(), or Add*() is called on a non-repeated
// field.
// - The Message object passed to any method is not of the right type for
// this Reflection object (i.e. message.GetReflection() != reflection).
//
// You might wonder why there is not any abstract representation for a field
// of arbitrary type. E.g., why isn't there just a "GetField()" method that
// returns "const Field&", where "Field" is some class with accessors like
// "GetInt32Value()". The problem is that someone would have to deal with
// allocating these Field objects. For generated message classes, having to
// allocate space for an additional object to wrap every field would at least
// double the message's memory footprint, probably worse. Allocating the
// objects on-demand, on the other hand, would be expensive and prone to
// memory leaks. So, instead we ended up with this flat interface.
//
// TODO(kenton): Create a utility class which callers can use to read and
// write fields from a Reflection without paying attention to the type.
class LIBPROTOBUF_EXPORT Reflection {
public:
// TODO(kenton): Remove parameter.
inline Reflection() {}
virtual ~Reflection();
// Get the UnknownFieldSet for the message. This contains fields which
// were seen when the Message was parsed but were not recognized according
// to the Message's definition.
virtual const UnknownFieldSet& GetUnknownFields(
const Message& message) const = 0;
// Get a mutable pointer to the UnknownFieldSet for the message. This
// contains fields which were seen when the Message was parsed but were not
// recognized according to the Message's definition.
virtual UnknownFieldSet* MutableUnknownFields(Message* message) const = 0;
// Estimate the amount of memory used by the message object.
virtual int SpaceUsed(const Message& message) const = 0;
// Check if the given non-repeated field is set.
virtual bool HasField(const Message& message,
const FieldDescriptor* field) const = 0;
// Get the number of elements of a repeated field.
virtual int FieldSize(const Message& message,
const FieldDescriptor* field) const = 0;
// Clear the value of a field, so that HasField() returns false or
// FieldSize() returns zero.
virtual void ClearField(Message* message,
const FieldDescriptor* field) const = 0;
// Remove the last element of a repeated field.
// We don't provide a way to remove any element other than the last
// because it invites inefficient use, such as O(n^2) filtering loops
// that should have been O(n). If you want to remove an element other
// than the last, the best way to do it is to re-arrange the elements
// (using Swap()) so that the one you want removed is at the end, then
// call RemoveLast().
virtual void RemoveLast(Message* message,
const FieldDescriptor* field) const = 0;
// Swap the complete contents of two messages.
virtual void Swap(Message* message1, Message* message2) const = 0;
// Swap two elements of a repeated field.
virtual void SwapElements(Message* message,
const FieldDescriptor* field,
int index1,
int index2) const = 0;
// List all fields of the message which are currently set. This includes
// extensions. Singular fields will only be listed if HasField(field) would
// return true and repeated fields will only be listed if FieldSize(field)
// would return non-zero. Fields (both normal fields and extension fields)
// will be listed ordered by field number.
virtual void ListFields(const Message& message,
vector<const FieldDescriptor*>* output) const = 0;
// Singular field getters ------------------------------------------
// These get the value of a non-repeated field. They return the default
// value for fields that aren't set.
virtual int32 GetInt32 (const Message& message,
const FieldDescriptor* field) const = 0;
virtual int64 GetInt64 (const Message& message,
const FieldDescriptor* field) const = 0;
virtual uint32 GetUInt32(const Message& message,
const FieldDescriptor* field) const = 0;
virtual uint64 GetUInt64(const Message& message,
const FieldDescriptor* field) const = 0;
virtual float GetFloat (const Message& message,
const FieldDescriptor* field) const = 0;
virtual double GetDouble(const Message& message,
const FieldDescriptor* field) const = 0;
virtual bool GetBool (const Message& message,
const FieldDescriptor* field) const = 0;
virtual string GetString(const Message& message,
const FieldDescriptor* field) const = 0;
virtual const EnumValueDescriptor* GetEnum(
const Message& message, const FieldDescriptor* field) const = 0;
// See MutableMessage() for the meaning of the "factory" parameter.
virtual const Message& GetMessage(const Message& message,
const FieldDescriptor* field,
MessageFactory* factory = NULL) const = 0;
// Get a string value without copying, if possible.
//
// GetString() necessarily returns a copy of the string. This can be
// inefficient when the string is already stored in a string object in the
// underlying message. GetStringReference() will return a reference to the
// underlying string in this case. Otherwise, it will copy the string into
// *scratch and return that.
//
// Note: It is perfectly reasonable and useful to write code like:
// str = reflection->GetStringReference(field, &str);
// This line would ensure that only one copy of the string is made
// regardless of the field's underlying representation. When initializing
// a newly-constructed string, though, it's just as fast and more readable
// to use code like:
// string str = reflection->GetString(field);
virtual const string& GetStringReference(const Message& message,
const FieldDescriptor* field,
string* scratch) const = 0;
// Singular field mutators -----------------------------------------
// These mutate the value of a non-repeated field.
virtual void SetInt32 (Message* message,
const FieldDescriptor* field, int32 value) const = 0;
virtual void SetInt64 (Message* message,
const FieldDescriptor* field, int64 value) const = 0;
virtual void SetUInt32(Message* message,
const FieldDescriptor* field, uint32 value) const = 0;
virtual void SetUInt64(Message* message,
const FieldDescriptor* field, uint64 value) const = 0;
virtual void SetFloat (Message* message,
const FieldDescriptor* field, float value) const = 0;
virtual void SetDouble(Message* message,
const FieldDescriptor* field, double value) const = 0;
virtual void SetBool (Message* message,
const FieldDescriptor* field, bool value) const = 0;
virtual void SetString(Message* message,
const FieldDescriptor* field,
const string& value) const = 0;
virtual void SetEnum (Message* message,
const FieldDescriptor* field,
const EnumValueDescriptor* value) const = 0;
// Get a mutable pointer to a field with a message type. If a MessageFactory
// is provided, it will be used to construct instances of the sub-message;
// otherwise, the default factory is used. If the field is an extension that
// does not live in the same pool as the containing message's descriptor (e.g.
// it lives in an overlay pool), then a MessageFactory must be provided.
// If you have no idea what that meant, then you probably don't need to worry
// about it (don't provide a MessageFactory). WARNING: If the
// FieldDescriptor is for a compiled-in extension, then
// factory->GetPrototype(field->message_type() MUST return an instance of the
// compiled-in class for this type, NOT DynamicMessage.
virtual Message* MutableMessage(Message* message,
const FieldDescriptor* field,
MessageFactory* factory = NULL) const = 0;
// Repeated field getters ------------------------------------------
// These get the value of one element of a repeated field.
virtual int32 GetRepeatedInt32 (const Message& message,
const FieldDescriptor* field,
int index) const = 0;
virtual int64 GetRepeatedInt64 (const Message& message,
const FieldDescriptor* field,
int index) const = 0;
virtual uint32 GetRepeatedUInt32(const Message& message,
const FieldDescriptor* field,
int index) const = 0;
virtual uint64 GetRepeatedUInt64(const Message& message,
const FieldDescriptor* field,
int index) const = 0;
virtual float GetRepeatedFloat (const Message& message,
const FieldDescriptor* field,
int index) const = 0;
virtual double GetRepeatedDouble(const Message& message,
const FieldDescriptor* field,
int index) const = 0;
virtual bool GetRepeatedBool (const Message& message,
const FieldDescriptor* field,
int index) const = 0;
virtual string GetRepeatedString(const Message& message,
const FieldDescriptor* field,
int index) const = 0;
virtual const EnumValueDescriptor* GetRepeatedEnum(
const Message& message,
const FieldDescriptor* field, int index) const = 0;
virtual const Message& GetRepeatedMessage(
const Message& message,
const FieldDescriptor* field, int index) const = 0;
// See GetStringReference(), above.
virtual const string& GetRepeatedStringReference(
const Message& message, const FieldDescriptor* field,
int index, string* scratch) const = 0;
// Repeated field mutators -----------------------------------------
// These mutate the value of one element of a repeated field.
virtual void SetRepeatedInt32 (Message* message,
const FieldDescriptor* field,
int index, int32 value) const = 0;
virtual void SetRepeatedInt64 (Message* message,
const FieldDescriptor* field,
int index, int64 value) const = 0;
virtual void SetRepeatedUInt32(Message* message,
const FieldDescriptor* field,
int index, uint32 value) const = 0;
virtual void SetRepeatedUInt64(Message* message,
const FieldDescriptor* field,
int index, uint64 value) const = 0;
virtual void SetRepeatedFloat (Message* message,
const FieldDescriptor* field,
int index, float value) const = 0;
virtual void SetRepeatedDouble(Message* message,
const FieldDescriptor* field,
int index, double value) const = 0;
virtual void SetRepeatedBool (Message* message,
const FieldDescriptor* field,
int index, bool value) const = 0;
virtual void SetRepeatedString(Message* message,
const FieldDescriptor* field,
int index, const string& value) const = 0;
virtual void SetRepeatedEnum(Message* message,
const FieldDescriptor* field, int index,
const EnumValueDescriptor* value) const = 0;
// Get a mutable pointer to an element of a repeated field with a message
// type.
virtual Message* MutableRepeatedMessage(
Message* message, const FieldDescriptor* field, int index) const = 0;
// Repeated field adders -------------------------------------------
// These add an element to a repeated field.
virtual void AddInt32 (Message* message,
const FieldDescriptor* field, int32 value) const = 0;
virtual void AddInt64 (Message* message,
const FieldDescriptor* field, int64 value) const = 0;
virtual void AddUInt32(Message* message,
const FieldDescriptor* field, uint32 value) const = 0;
virtual void AddUInt64(Message* message,
const FieldDescriptor* field, uint64 value) const = 0;
virtual void AddFloat (Message* message,
const FieldDescriptor* field, float value) const = 0;
virtual void AddDouble(Message* message,
const FieldDescriptor* field, double value) const = 0;
virtual void AddBool (Message* message,
const FieldDescriptor* field, bool value) const = 0;
virtual void AddString(Message* message,
const FieldDescriptor* field,
const string& value) const = 0;
virtual void AddEnum (Message* message,
const FieldDescriptor* field,
const EnumValueDescriptor* value) const = 0;
// See MutableMessage() for comments on the "factory" parameter.
virtual Message* AddMessage(Message* message,
const FieldDescriptor* field,
MessageFactory* factory = NULL) const = 0;
// Extensions ------------------------------------------------------
// Try to find an extension of this message type by fully-qualified field
// name. Returns NULL if no extension is known for this name or number.
virtual const FieldDescriptor* FindKnownExtensionByName(
const string& name) const = 0;
// Try to find an extension of this message type by field number.
// Returns NULL if no extension is known for this name or number.
virtual const FieldDescriptor* FindKnownExtensionByNumber(
int number) const = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Reflection);
};
// Abstract interface for a factory for message objects.
class LIBPROTOBUF_EXPORT MessageFactory {
public:
inline MessageFactory() {}
virtual ~MessageFactory();
// Given a Descriptor, gets or constructs the default (prototype) Message
// of that type. You can then call that message's New() method to construct
// a mutable message of that type.
//
// Calling this method twice with the same Descriptor returns the same
// object. The returned object remains property of the factory. Also, any
// objects created by calling the prototype's New() method share some data
// with the prototype, so these must be destoyed before the MessageFactory
// is destroyed.
//
// The given descriptor must outlive the returned message, and hence must
// outlive the MessageFactory.
//
// Some implementations do not support all types. GetPrototype() will
// return NULL if the descriptor passed in is not supported.
//
// This method may or may not be thread-safe depending on the implementation.
// Each implementation should document its own degree thread-safety.
virtual const Message* GetPrototype(const Descriptor* type) = 0;
// Gets a MessageFactory which supports all generated, compiled-in messages.
// In other words, for any compiled-in type FooMessage, the following is true:
// MessageFactory::generated_factory()->GetPrototype(
// FooMessage::descriptor()) == FooMessage::default_instance()
// This factory supports all types which are found in
// DescriptorPool::generated_pool(). If given a descriptor from any other
// pool, GetPrototype() will return NULL. (You can also check if a
// descriptor is for a generated message by checking if
// descriptor->file()->pool() == DescriptorPool::generated_pool().)
//
// This factory is 100% thread-safe; calling GetPrototype() does not modify
// any shared data.
//
// This factory is a singleton. The caller must not delete the object.
static MessageFactory* generated_factory();
// For internal use only: Registers a .proto file at static initialization
// time, to be placed in generated_factory. The first time GetPrototype()
// is called with a descriptor from this file, |register_messages| will be
// called, with the file name as the parameter. It must call
// InternalRegisterGeneratedMessage() (below) to register each message type
// in the file. This strange mechanism is necessary because descriptors are
// built lazily, so we can't register types by their descriptor until we
// know that the descriptor exists. |filename| must be a permanent string.
static void InternalRegisterGeneratedFile(
const char* filename, void (*register_messages)(const string&));
// For internal use only: Registers a message type. Called only by the
// functions which are registered with InternalRegisterGeneratedFile(),
// above.
static void InternalRegisterGeneratedMessage(const Descriptor* descriptor,
const Message* prototype);
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFactory);
};
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_MESSAGE_H__

View file

@ -0,0 +1,334 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Authors: wink@google.com (Wink Saville),
// kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/message_lite.h>
#include <string>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
namespace protobuf {
MessageLite::~MessageLite() {}
string MessageLite::InitializationErrorString() const {
return "(cannot determine missing fields for lite message)";
}
namespace {
// When serializing, we first compute the byte size, then serialize the message.
// If serialization produces a different number of bytes than expected, we
// call this function, which crashes. The problem could be due to a bug in the
// protobuf implementation but is more likely caused by concurrent modification
// of the message. This function attempts to distinguish between the two and
// provide a useful error message.
void ByteSizeConsistencyError(int byte_size_before_serialization,
int byte_size_after_serialization,
int bytes_produced_by_serialization) {
GOOGLE_CHECK_EQ(byte_size_before_serialization, byte_size_after_serialization)
<< "Protocol message was modified concurrently during serialization.";
GOOGLE_CHECK_EQ(bytes_produced_by_serialization, byte_size_before_serialization)
<< "Byte size calculation and serialization were inconsistent. This "
"may indicate a bug in protocol buffers or it may be caused by "
"concurrent modification of the message.";
GOOGLE_LOG(FATAL) << "This shouldn't be called if all the sizes are equal.";
}
string InitializationErrorMessage(const char* action,
const MessageLite& message) {
// Note: We want to avoid depending on strutil in the lite library, otherwise
// we'd use:
//
// return strings::Substitute(
// "Can't $0 message of type \"$1\" because it is missing required "
// "fields: $2",
// action, message.GetTypeName(),
// message.InitializationErrorString());
string result;
result += "Can't ";
result += action;
result += " message of type \"";
result += message.GetTypeName();
result += "\" because it is missing required fields: ";
result += message.InitializationErrorString();
return result;
}
// Several of the Parse methods below just do one thing and then call another
// method. In a naive implementation, we might have ParseFromString() call
// ParseFromArray() which would call ParseFromZeroCopyStream() which would call
// ParseFromCodedStream() which would call MergeFromCodedStream() which would
// call MergePartialFromCodedStream(). However, when parsing very small
// messages, every function call introduces significant overhead. To avoid
// this without reproducing code, we use these forced-inline helpers.
//
// Note: GCC only allows GOOGLE_ATTRIBUTE_ALWAYS_INLINE on declarations, not
// definitions.
inline bool InlineMergeFromCodedStream(io::CodedInputStream* input,
MessageLite* message)
GOOGLE_ATTRIBUTE_ALWAYS_INLINE;
inline bool InlineParseFromCodedStream(io::CodedInputStream* input,
MessageLite* message)
GOOGLE_ATTRIBUTE_ALWAYS_INLINE;
inline bool InlineParsePartialFromCodedStream(io::CodedInputStream* input,
MessageLite* message)
GOOGLE_ATTRIBUTE_ALWAYS_INLINE;
inline bool InlineParseFromArray(const void* data, int size,
MessageLite* message)
GOOGLE_ATTRIBUTE_ALWAYS_INLINE;
inline bool InlineParsePartialFromArray(const void* data, int size,
MessageLite* message)
GOOGLE_ATTRIBUTE_ALWAYS_INLINE;
bool InlineMergeFromCodedStream(io::CodedInputStream* input,
MessageLite* message) {
if (!message->MergePartialFromCodedStream(input)) return false;
if (!message->IsInitialized()) {
GOOGLE_LOG(ERROR) << InitializationErrorMessage("parse", *message);
return false;
}
return true;
}
bool InlineParseFromCodedStream(io::CodedInputStream* input,
MessageLite* message) {
message->Clear();
return InlineMergeFromCodedStream(input, message);
}
bool InlineParsePartialFromCodedStream(io::CodedInputStream* input,
MessageLite* message) {
message->Clear();
return message->MergePartialFromCodedStream(input);
}
bool InlineParseFromArray(const void* data, int size, MessageLite* message) {
io::CodedInputStream input(reinterpret_cast<const uint8*>(data), size);
return InlineParseFromCodedStream(&input, message) &&
input.ConsumedEntireMessage();
}
bool InlineParsePartialFromArray(const void* data, int size,
MessageLite* message) {
io::CodedInputStream input(reinterpret_cast<const uint8*>(data), size);
return InlineParsePartialFromCodedStream(&input, message) &&
input.ConsumedEntireMessage();
}
} // namespace
bool MessageLite::MergeFromCodedStream(io::CodedInputStream* input) {
return InlineMergeFromCodedStream(input, this);
}
bool MessageLite::ParseFromCodedStream(io::CodedInputStream* input) {
return InlineParseFromCodedStream(input, this);
}
bool MessageLite::ParsePartialFromCodedStream(io::CodedInputStream* input) {
return InlineParsePartialFromCodedStream(input, this);
}
bool MessageLite::ParseFromZeroCopyStream(io::ZeroCopyInputStream* input) {
io::CodedInputStream decoder(input);
return ParseFromCodedStream(&decoder) && decoder.ConsumedEntireMessage();
}
bool MessageLite::ParsePartialFromZeroCopyStream(
io::ZeroCopyInputStream* input) {
io::CodedInputStream decoder(input);
return ParsePartialFromCodedStream(&decoder) &&
decoder.ConsumedEntireMessage();
}
bool MessageLite::ParseFromBoundedZeroCopyStream(
io::ZeroCopyInputStream* input, int size) {
io::CodedInputStream decoder(input);
decoder.PushLimit(size);
return ParseFromCodedStream(&decoder) &&
decoder.ConsumedEntireMessage() &&
decoder.BytesUntilLimit() == 0;
}
bool MessageLite::ParsePartialFromBoundedZeroCopyStream(
io::ZeroCopyInputStream* input, int size) {
io::CodedInputStream decoder(input);
decoder.PushLimit(size);
return ParsePartialFromCodedStream(&decoder) &&
decoder.ConsumedEntireMessage() &&
decoder.BytesUntilLimit() == 0;
}
bool MessageLite::ParseFromString(const string& data) {
return InlineParseFromArray(data.data(), data.size(), this);
}
bool MessageLite::ParsePartialFromString(const string& data) {
return InlineParsePartialFromArray(data.data(), data.size(), this);
}
bool MessageLite::ParseFromArray(const void* data, int size) {
return InlineParseFromArray(data, size, this);
}
bool MessageLite::ParsePartialFromArray(const void* data, int size) {
return InlineParsePartialFromArray(data, size, this);
}
// ===================================================================
uint8* MessageLite::SerializeWithCachedSizesToArray(uint8* target) const {
// We only optimize this when using optimize_for = SPEED. In other cases
// we just use the CodedOutputStream path.
int size = GetCachedSize();
io::ArrayOutputStream out(target, size);
io::CodedOutputStream coded_out(&out);
SerializeWithCachedSizes(&coded_out);
GOOGLE_CHECK(!coded_out.HadError());
return target + size;
}
bool MessageLite::SerializeToCodedStream(io::CodedOutputStream* output) const {
GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this);
return SerializePartialToCodedStream(output);
}
bool MessageLite::SerializePartialToCodedStream(
io::CodedOutputStream* output) const {
const int size = ByteSize(); // Force size to be cached.
uint8* buffer = output->GetDirectBufferForNBytesAndAdvance(size);
if (buffer != NULL) {
uint8* end = SerializeWithCachedSizesToArray(buffer);
if (end - buffer != size) {
ByteSizeConsistencyError(size, ByteSize(), end - buffer);
}
return true;
} else {
int original_byte_count = output->ByteCount();
SerializeWithCachedSizes(output);
if (output->HadError()) {
return false;
}
int final_byte_count = output->ByteCount();
if (final_byte_count - original_byte_count != size) {
ByteSizeConsistencyError(size, ByteSize(),
final_byte_count - original_byte_count);
}
return true;
}
}
bool MessageLite::SerializeToZeroCopyStream(
io::ZeroCopyOutputStream* output) const {
io::CodedOutputStream encoder(output);
return SerializeToCodedStream(&encoder);
}
bool MessageLite::SerializePartialToZeroCopyStream(
io::ZeroCopyOutputStream* output) const {
io::CodedOutputStream encoder(output);
return SerializePartialToCodedStream(&encoder);
}
bool MessageLite::AppendToString(string* output) const {
GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this);
return AppendPartialToString(output);
}
bool MessageLite::AppendPartialToString(string* output) const {
int old_size = output->size();
int byte_size = ByteSize();
STLStringResizeUninitialized(output, old_size + byte_size);
uint8* start = reinterpret_cast<uint8*>(string_as_array(output) + old_size);
uint8* end = SerializeWithCachedSizesToArray(start);
if (end - start != byte_size) {
ByteSizeConsistencyError(byte_size, ByteSize(), end - start);
}
return true;
}
bool MessageLite::SerializeToString(string* output) const {
output->clear();
return AppendToString(output);
}
bool MessageLite::SerializePartialToString(string* output) const {
output->clear();
return AppendPartialToString(output);
}
bool MessageLite::SerializeToArray(void* data, int size) const {
GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this);
return SerializePartialToArray(data, size);
}
bool MessageLite::SerializePartialToArray(void* data, int size) const {
int byte_size = ByteSize();
if (size < byte_size) return false;
uint8* start = reinterpret_cast<uint8*>(data);
uint8* end = SerializeWithCachedSizesToArray(start);
if (end - start != byte_size) {
ByteSizeConsistencyError(byte_size, ByteSize(), end - start);
}
return true;
}
string MessageLite::SerializeAsString() const {
// If the compiler implements the (Named) Return Value Optimization,
// the local variable 'result' will not actually reside on the stack
// of this function, but will be overlaid with the object that the
// caller supplied for the return value to be constructed in.
string output;
if (!AppendToString(&output))
output.clear();
return output;
}
string MessageLite::SerializePartialAsString() const {
string output;
if (!AppendPartialToString(&output))
output.clear();
return output;
}
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,239 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Authors: wink@google.com (Wink Saville),
// kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Defines MessageLite, the abstract interface implemented by all (lite
// and non-lite) protocol message objects.
#ifndef GOOGLE_PROTOBUF_MESSAGE_LITE_H__
#define GOOGLE_PROTOBUF_MESSAGE_LITE_H__
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/io/coded_stream.h>
namespace google {
namespace protobuf {
// Interface to light weight protocol messages.
//
// This interface is implemented by all protocol message objects. Non-lite
// messages additionally implement the Message interface, which is a
// subclass of MessageLite. Use MessageLite instead when you only need
// the subset of features which it supports -- namely, nothing that uses
// descriptors or reflection. You can instruct the protocol compiler
// to generate classes which implement only MessageLite, not the full
// Message interface, by adding the following line to the .proto file:
//
// option optimize_for = LITE_RUNTIME;
//
// This is particularly useful on resource-constrained systems where
// the full protocol buffers runtime library is too big.
//
// Note that on non-constrained systems (e.g. servers) when you need
// to link in lots of protocol definitions, a better way to reduce
// total code footprint is to use optimize_for = CODE_SIZE. This
// will make the generated code smaller while still supporting all the
// same features (at the expense of speed). optimize_for = LITE_RUNTIME
// is best when you only have a small number of message types linked
// into your binary, in which case the size of the protocol buffers
// runtime itself is the biggest problem.
class LIBPROTOBUF_EXPORT MessageLite {
public:
inline MessageLite() {}
virtual ~MessageLite();
// Basic Operations ------------------------------------------------
// Get the name of this message type, e.g. "foo.bar.BazProto".
virtual string GetTypeName() const = 0;
// Construct a new instance of the same type. Ownership is passed to the
// caller.
virtual MessageLite* New() const = 0;
// Clear all fields of the message and set them to their default values.
// Clear() avoids freeing memory, assuming that any memory allocated
// to hold parts of the message will be needed again to hold the next
// message. If you actually want to free the memory used by a Message,
// you must delete it.
virtual void Clear() = 0;
// Quickly check if all required fields have values set.
virtual bool IsInitialized() const = 0;
// This is not implemented for Lite messages -- it just returns "(cannot
// determine missing fields for lite message)". However, it is implemented
// for full messages. See message.h.
virtual string InitializationErrorString() const;
// If |other| is the exact same class as this, calls MergeFrom(). Otherwise,
// results are undefined (probably crash).
virtual void CheckTypeAndMergeFrom(const MessageLite& other) = 0;
// Parsing ---------------------------------------------------------
// Methods for parsing in protocol buffer format. Most of these are
// just simple wrappers around MergeFromCodedStream().
// Fill the message with a protocol buffer parsed from the given input
// stream. Returns false on a read error or if the input is in the
// wrong format.
bool ParseFromCodedStream(io::CodedInputStream* input);
// Like ParseFromCodedStream(), but accepts messages that are missing
// required fields.
bool ParsePartialFromCodedStream(io::CodedInputStream* input);
// Read a protocol buffer from the given zero-copy input stream. If
// successful, the entire input will be consumed.
bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input);
// Like ParseFromZeroCopyStream(), but accepts messages that are missing
// required fields.
bool ParsePartialFromZeroCopyStream(io::ZeroCopyInputStream* input);
// Read a protocol buffer from the given zero-copy input stream, expecting
// the message to be exactly "size" bytes long. If successful, exactly
// this many bytes will have been consumed from the input.
bool ParseFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input, int size);
// Like ParseFromBoundedZeroCopyStream(), but accepts messages that are
// missing required fields.
bool ParsePartialFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input,
int size);
// Parse a protocol buffer contained in a string.
bool ParseFromString(const string& data);
// Like ParseFromString(), but accepts messages that are missing
// required fields.
bool ParsePartialFromString(const string& data);
// Parse a protocol buffer contained in an array of bytes.
bool ParseFromArray(const void* data, int size);
// Like ParseFromArray(), but accepts messages that are missing
// required fields.
bool ParsePartialFromArray(const void* data, int size);
// Reads a protocol buffer from the stream and merges it into this
// Message. Singular fields read from the input overwrite what is
// already in the Message and repeated fields are appended to those
// already present.
//
// It is the responsibility of the caller to call input->LastTagWas()
// (for groups) or input->ConsumedEntireMessage() (for non-groups) after
// this returns to verify that the message's end was delimited correctly.
//
// ParsefromCodedStream() is implemented as Clear() followed by
// MergeFromCodedStream().
bool MergeFromCodedStream(io::CodedInputStream* input);
// Like MergeFromCodedStream(), but succeeds even if required fields are
// missing in the input.
//
// MergeFromCodedStream() is just implemented as MergePartialFromCodedStream()
// followed by IsInitialized().
virtual bool MergePartialFromCodedStream(io::CodedInputStream* input) = 0;
// Serialization ---------------------------------------------------
// Methods for serializing in protocol buffer format. Most of these
// are just simple wrappers around ByteSize() and SerializeWithCachedSizes().
// Write a protocol buffer of this message to the given output. Returns
// false on a write error. If the message is missing required fields,
// this may GOOGLE_CHECK-fail.
bool SerializeToCodedStream(io::CodedOutputStream* output) const;
// Like SerializeToCodedStream(), but allows missing required fields.
bool SerializePartialToCodedStream(io::CodedOutputStream* output) const;
// Write the message to the given zero-copy output stream. All required
// fields must be set.
bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
// Like SerializeToZeroCopyStream(), but allows missing required fields.
bool SerializePartialToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
// Serialize the message and store it in the given string. All required
// fields must be set.
bool SerializeToString(string* output) const;
// Like SerializeToString(), but allows missing required fields.
bool SerializePartialToString(string* output) const;
// Serialize the message and store it in the given byte array. All required
// fields must be set.
bool SerializeToArray(void* data, int size) const;
// Like SerializeToArray(), but allows missing required fields.
bool SerializePartialToArray(void* data, int size) const;
// Make a string encoding the message. Is equivalent to calling
// SerializeToString() on a string and using that. Returns the empty
// string if SerializeToString() would have returned an error.
// Note: If you intend to generate many such strings, you may
// reduce heap fragmentation by instead re-using the same string
// object with calls to SerializeToString().
string SerializeAsString() const;
// Like SerializeAsString(), but allows missing required fields.
string SerializePartialAsString() const;
// Like SerializeToString(), but appends to the data to the string's existing
// contents. All required fields must be set.
bool AppendToString(string* output) const;
// Like AppendToString(), but allows missing required fields.
bool AppendPartialToString(string* output) const;
// Computes the serialized size of the message. This recursively calls
// ByteSize() on all embedded messages. If a subclass does not override
// this, it MUST override SetCachedSize().
virtual int ByteSize() const = 0;
// Serializes the message without recomputing the size. The message must
// not have changed since the last call to ByteSize(); if it has, the results
// are undefined.
virtual void SerializeWithCachedSizes(
io::CodedOutputStream* output) const = 0;
// Like SerializeWithCachedSizes, but writes directly to *target, returning
// a pointer to the byte immediately after the last byte written. "target"
// must point at a byte array of at least ByteSize() bytes.
virtual uint8* SerializeWithCachedSizesToArray(uint8* target) const;
// Returns the result of the last call to ByteSize(). An embedded message's
// size is needed both to serialize it (because embedded messages are
// length-delimited) and to compute the outer message's size. Caching
// the size avoids computing it multiple times.
//
// ByteSize() does not automatically use the cached size when available
// because this would require invalidating it every time the message was
// modified, which would be too hard and expensive. (E.g. if a deeply-nested
// sub-message is changed, all of its parents' cached sizes would need to be
// invalidated, which is too much work for an otherwise inlined setter
// method.)
virtual int GetCachedSize() const = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageLite);
};
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_MESSAGE_LITE_H__

View file

@ -0,0 +1,281 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/message.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _MSC_VER
#include <io.h>
#else
#include <unistd.h>
#endif
#include <sstream>
#include <fstream>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
#ifndef O_BINARY
#ifdef _O_BINARY
#define O_BINARY _O_BINARY
#else
#define O_BINARY 0 // If this isn't defined, the platform doesn't need it.
#endif
#endif
TEST(MessageTest, SerializeHelpers) {
// TODO(kenton): Test more helpers? They're all two-liners so it seems
// like a waste of time.
protobuf_unittest::TestAllTypes message;
TestUtil::SetAllFields(&message);
stringstream stream;
string str1("foo");
string str2("bar");
EXPECT_TRUE(message.SerializeToString(&str1));
EXPECT_TRUE(message.AppendToString(&str2));
EXPECT_TRUE(message.SerializeToOstream(&stream));
EXPECT_EQ(str1.size() + 3, str2.size());
EXPECT_EQ("bar", str2.substr(0, 3));
// Don't use EXPECT_EQ because we don't want to dump raw binary data to
// stdout.
EXPECT_TRUE(str2.substr(3) == str1);
// GCC gives some sort of error if we try to just do stream.str() == str1.
string temp = stream.str();
EXPECT_TRUE(temp == str1);
EXPECT_TRUE(message.SerializeAsString() == str1);
}
TEST(MessageTest, SerializeToBrokenOstream) {
ofstream out;
protobuf_unittest::TestAllTypes message;
message.set_optional_int32(123);
EXPECT_FALSE(message.SerializeToOstream(&out));
}
TEST(MessageTest, ParseFromFileDescriptor) {
string filename = TestSourceDir() +
"/google/protobuf/testdata/golden_message";
int file = open(filename.c_str(), O_RDONLY | O_BINARY);
unittest::TestAllTypes message;
EXPECT_TRUE(message.ParseFromFileDescriptor(file));
TestUtil::ExpectAllFieldsSet(message);
EXPECT_GE(close(file), 0);
}
TEST(MessageTest, ParsePackedFromFileDescriptor) {
string filename =
TestSourceDir() +
"/google/protobuf/testdata/golden_packed_fields_message";
int file = open(filename.c_str(), O_RDONLY | O_BINARY);
unittest::TestPackedTypes message;
EXPECT_TRUE(message.ParseFromFileDescriptor(file));
TestUtil::ExpectPackedFieldsSet(message);
EXPECT_GE(close(file), 0);
}
TEST(MessageTest, ParseHelpers) {
// TODO(kenton): Test more helpers? They're all two-liners so it seems
// like a waste of time.
string data;
{
// Set up.
protobuf_unittest::TestAllTypes message;
TestUtil::SetAllFields(&message);
message.SerializeToString(&data);
}
{
// Test ParseFromString.
protobuf_unittest::TestAllTypes message;
EXPECT_TRUE(message.ParseFromString(data));
TestUtil::ExpectAllFieldsSet(message);
}
{
// Test ParseFromIstream.
protobuf_unittest::TestAllTypes message;
stringstream stream(data);
EXPECT_TRUE(message.ParseFromIstream(&stream));
EXPECT_TRUE(stream.eof());
TestUtil::ExpectAllFieldsSet(message);
}
{
// Test ParseFromBoundedZeroCopyStream.
string data_with_junk(data);
data_with_junk.append("some junk on the end");
io::ArrayInputStream stream(data_with_junk.data(), data_with_junk.size());
protobuf_unittest::TestAllTypes message;
EXPECT_TRUE(message.ParseFromBoundedZeroCopyStream(&stream, data.size()));
TestUtil::ExpectAllFieldsSet(message);
}
{
// Test that ParseFromBoundedZeroCopyStream fails (but doesn't crash) if
// EOF is reached before the expected number of bytes.
io::ArrayInputStream stream(data.data(), data.size());
protobuf_unittest::TestAllTypes message;
EXPECT_FALSE(
message.ParseFromBoundedZeroCopyStream(&stream, data.size() + 1));
}
}
TEST(MessageTest, ParseFailsIfNotInitialized) {
unittest::TestRequired message;
vector<string> errors;
{
ScopedMemoryLog log;
EXPECT_FALSE(message.ParseFromString(""));
errors = log.GetMessages(ERROR);
}
ASSERT_EQ(1, errors.size());
EXPECT_EQ("Can't parse message of type \"protobuf_unittest.TestRequired\" "
"because it is missing required fields: a, b, c",
errors[0]);
}
TEST(MessageTest, BypassInitializationCheckOnParse) {
unittest::TestRequired message;
io::ArrayInputStream raw_input(NULL, 0);
io::CodedInputStream input(&raw_input);
EXPECT_TRUE(message.MergePartialFromCodedStream(&input));
}
TEST(MessageTest, InitializationErrorString) {
unittest::TestRequired message;
EXPECT_EQ("a, b, c", message.InitializationErrorString());
}
#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet.
TEST(MessageTest, SerializeFailsIfNotInitialized) {
unittest::TestRequired message;
string data;
EXPECT_DEBUG_DEATH(EXPECT_TRUE(message.SerializeToString(&data)),
"Can't serialize message of type \"protobuf_unittest.TestRequired\" because "
"it is missing required fields: a, b, c");
}
TEST(MessageTest, CheckInitialized) {
unittest::TestRequired message;
EXPECT_DEATH(message.CheckInitialized(),
"Message of type \"protobuf_unittest.TestRequired\" is missing required "
"fields: a, b, c");
}
#endif // GTEST_HAS_DEATH_TEST
TEST(MessageTest, BypassInitializationCheckOnSerialize) {
unittest::TestRequired message;
io::ArrayOutputStream raw_output(NULL, 0);
io::CodedOutputStream output(&raw_output);
EXPECT_TRUE(message.SerializePartialToCodedStream(&output));
}
TEST(MessageTest, FindInitializationErrors) {
unittest::TestRequired message;
vector<string> errors;
message.FindInitializationErrors(&errors);
ASSERT_EQ(3, errors.size());
EXPECT_EQ("a", errors[0]);
EXPECT_EQ("b", errors[1]);
EXPECT_EQ("c", errors[2]);
}
TEST(MessageTest, ParseFailsOnInvalidMessageEnd) {
unittest::TestAllTypes message;
// Control case.
EXPECT_TRUE(message.ParseFromArray("", 0));
// The byte is a valid varint, but not a valid tag (zero).
EXPECT_FALSE(message.ParseFromArray("\0", 1));
// The byte is a malformed varint.
EXPECT_FALSE(message.ParseFromArray("\200", 1));
// The byte is an endgroup tag, but we aren't parsing a group.
EXPECT_FALSE(message.ParseFromArray("\014", 1));
}
TEST(MessageFactoryTest, GeneratedFactoryLookup) {
EXPECT_EQ(
MessageFactory::generated_factory()->GetPrototype(
protobuf_unittest::TestAllTypes::descriptor()),
&protobuf_unittest::TestAllTypes::default_instance());
}
TEST(MessageFactoryTest, GeneratedFactoryUnknownType) {
// Construct a new descriptor.
DescriptorPool pool;
FileDescriptorProto file;
file.set_name("foo.proto");
file.add_message_type()->set_name("Foo");
const Descriptor* descriptor = pool.BuildFile(file)->message_type(0);
// Trying to construct it should return NULL.
EXPECT_TRUE(
MessageFactory::generated_factory()->GetPrototype(descriptor) == NULL);
}
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,64 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This file exists solely to document the google::protobuf namespace.
// It is not compiled into anything, but it may be read by an automated
// documentation generator.
namespace google {
// Core components of the Protocol Buffers runtime library.
//
// The files in this package represent the core of the Protocol Buffer
// system. All of them are part of the libprotobuf library.
//
// A note on thread-safety:
//
// Thread-safety in the Protocol Buffer library follows a simple rule:
// unless explicitly noted otherwise, it is always safe to use an object
// from multiple threads simultaneously as long as the object is declared
// const in all threads (or, it is only used in ways that would be allowed
// if it were declared const). However, if an object is accessed in one
// thread in a way that would not be allowed if it were const, then it is
// not safe to access that object in any other thread simultaneously.
//
// Put simply, read-only access to an object can happen in multiple threads
// simultaneously, but write access can only happen in a single thread at
// a time.
//
// The implementation does contain some "const" methods which actually modify
// the object behind the scenes -- e.g., to cache results -- but in these cases
// mutex locking is used to make the access thread-safe.
namespace protobuf {}
} // namespace google

View file

@ -0,0 +1,262 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/reflection_ops.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/unknown_field_set.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace internal {
void ReflectionOps::Copy(const Message& from, Message* to) {
if (&from == to) return;
Clear(to);
Merge(from, to);
}
void ReflectionOps::Merge(const Message& from, Message* to) {
GOOGLE_CHECK_NE(&from, to);
const Descriptor* descriptor = from.GetDescriptor();
GOOGLE_CHECK_EQ(to->GetDescriptor(), descriptor)
<< "Tried to merge messages of different types.";
const Reflection* from_reflection = from.GetReflection();
const Reflection* to_reflection = to->GetReflection();
vector<const FieldDescriptor*> fields;
from_reflection->ListFields(from, &fields);
for (int i = 0; i < fields.size(); i++) {
const FieldDescriptor* field = fields[i];
if (field->is_repeated()) {
int count = from_reflection->FieldSize(from, field);
for (int j = 0; j < count; j++) {
switch (field->cpp_type()) {
#define HANDLE_TYPE(CPPTYPE, METHOD) \
case FieldDescriptor::CPPTYPE_##CPPTYPE: \
to_reflection->Add##METHOD(to, field, \
from_reflection->GetRepeated##METHOD(from, field, j)); \
break;
HANDLE_TYPE(INT32 , Int32 );
HANDLE_TYPE(INT64 , Int64 );
HANDLE_TYPE(UINT32, UInt32);
HANDLE_TYPE(UINT64, UInt64);
HANDLE_TYPE(FLOAT , Float );
HANDLE_TYPE(DOUBLE, Double);
HANDLE_TYPE(BOOL , Bool );
HANDLE_TYPE(STRING, String);
HANDLE_TYPE(ENUM , Enum );
#undef HANDLE_TYPE
case FieldDescriptor::CPPTYPE_MESSAGE:
to_reflection->AddMessage(to, field)->MergeFrom(
from_reflection->GetRepeatedMessage(from, field, j));
break;
}
}
} else {
switch (field->cpp_type()) {
#define HANDLE_TYPE(CPPTYPE, METHOD) \
case FieldDescriptor::CPPTYPE_##CPPTYPE: \
to_reflection->Set##METHOD(to, field, \
from_reflection->Get##METHOD(from, field)); \
break;
HANDLE_TYPE(INT32 , Int32 );
HANDLE_TYPE(INT64 , Int64 );
HANDLE_TYPE(UINT32, UInt32);
HANDLE_TYPE(UINT64, UInt64);
HANDLE_TYPE(FLOAT , Float );
HANDLE_TYPE(DOUBLE, Double);
HANDLE_TYPE(BOOL , Bool );
HANDLE_TYPE(STRING, String);
HANDLE_TYPE(ENUM , Enum );
#undef HANDLE_TYPE
case FieldDescriptor::CPPTYPE_MESSAGE:
to_reflection->MutableMessage(to, field)->MergeFrom(
from_reflection->GetMessage(from, field));
break;
}
}
}
to_reflection->MutableUnknownFields(to)->MergeFrom(
from_reflection->GetUnknownFields(from));
}
void ReflectionOps::Clear(Message* message) {
const Reflection* reflection = message->GetReflection();
vector<const FieldDescriptor*> fields;
reflection->ListFields(*message, &fields);
for (int i = 0; i < fields.size(); i++) {
reflection->ClearField(message, fields[i]);
}
reflection->MutableUnknownFields(message)->Clear();
}
bool ReflectionOps::IsInitialized(const Message& message) {
const Descriptor* descriptor = message.GetDescriptor();
const Reflection* reflection = message.GetReflection();
// Check required fields of this message.
for (int i = 0; i < descriptor->field_count(); i++) {
if (descriptor->field(i)->is_required()) {
if (!reflection->HasField(message, descriptor->field(i))) {
return false;
}
}
}
// Check that sub-messages are initialized.
vector<const FieldDescriptor*> fields;
reflection->ListFields(message, &fields);
for (int i = 0; i < fields.size(); i++) {
const FieldDescriptor* field = fields[i];
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
if (field->is_repeated()) {
int size = reflection->FieldSize(message, field);
for (int i = 0; i < size; i++) {
if (!reflection->GetRepeatedMessage(message, field, i)
.IsInitialized()) {
return false;
}
}
} else {
if (!reflection->GetMessage(message, field).IsInitialized()) {
return false;
}
}
}
}
return true;
}
void ReflectionOps::DiscardUnknownFields(Message* message) {
const Reflection* reflection = message->GetReflection();
reflection->MutableUnknownFields(message)->Clear();
vector<const FieldDescriptor*> fields;
reflection->ListFields(*message, &fields);
for (int i = 0; i < fields.size(); i++) {
const FieldDescriptor* field = fields[i];
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
if (field->is_repeated()) {
int size = reflection->FieldSize(*message, field);
for (int i = 0; i < size; i++) {
reflection->MutableRepeatedMessage(message, field, i)
->DiscardUnknownFields();
}
} else {
reflection->MutableMessage(message, field)->DiscardUnknownFields();
}
}
}
}
static string SubMessagePrefix(const string& prefix,
const FieldDescriptor* field,
int index) {
string result(prefix);
if (field->is_extension()) {
result.append("(");
result.append(field->full_name());
result.append(")");
} else {
result.append(field->name());
}
if (index != -1) {
result.append("[");
result.append(SimpleItoa(index));
result.append("]");
}
result.append(".");
return result;
}
void ReflectionOps::FindInitializationErrors(
const Message& message,
const string& prefix,
vector<string>* errors) {
const Descriptor* descriptor = message.GetDescriptor();
const Reflection* reflection = message.GetReflection();
// Check required fields of this message.
for (int i = 0; i < descriptor->field_count(); i++) {
if (descriptor->field(i)->is_required()) {
if (!reflection->HasField(message, descriptor->field(i))) {
errors->push_back(prefix + descriptor->field(i)->name());
}
}
}
// Check sub-messages.
vector<const FieldDescriptor*> fields;
reflection->ListFields(message, &fields);
for (int i = 0; i < fields.size(); i++) {
const FieldDescriptor* field = fields[i];
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
if (field->is_repeated()) {
int size = reflection->FieldSize(message, field);
for (int i = 0; i < size; i++) {
const Message& sub_message =
reflection->GetRepeatedMessage(message, field, i);
FindInitializationErrors(sub_message,
SubMessagePrefix(prefix, field, i),
errors);
}
} else {
const Message& sub_message = reflection->GetMessage(message, field);
FindInitializationErrors(sub_message,
SubMessagePrefix(prefix, field, -1),
errors);
}
}
}
}
} // namespace internal
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,80 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This header is logically internal, but is made public because it is used
// from protocol-compiler-generated code, which may reside in other components.
#ifndef GOOGLE_PROTOBUF_REFLECTION_OPS_H__
#define GOOGLE_PROTOBUF_REFLECTION_OPS_H__
#include <google/protobuf/message.h>
namespace google {
namespace protobuf {
namespace internal {
// Basic operations that can be performed using reflection.
// These can be used as a cheap way to implement the corresponding
// methods of the Message interface, though they are likely to be
// slower than implementations tailored for the specific message type.
//
// This class should stay limited to operations needed to implement
// the Message interface.
//
// This class is really a namespace that contains only static methods.
class LIBPROTOBUF_EXPORT ReflectionOps {
public:
static void Copy(const Message& from, Message* to);
static void Merge(const Message& from, Message* to);
static void Clear(Message* message);
static bool IsInitialized(const Message& message);
static void DiscardUnknownFields(Message* message);
// Finds all unset required fields in the message and adds their full
// paths (e.g. "foo.bar[5].baz") to *names. "prefix" will be attached to
// the front of each name.
static void FindInitializationErrors(const Message& message,
const string& prefix,
vector<string>* errors);
private:
// All methods are static. No need to construct.
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ReflectionOps);
};
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_REFLECTION_OPS_H__

View file

@ -0,0 +1,405 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/reflection_ops.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace internal {
namespace {
TEST(ReflectionOpsTest, SanityCheck) {
unittest::TestAllTypes message;
TestUtil::SetAllFields(&message);
TestUtil::ExpectAllFieldsSet(message);
}
TEST(ReflectionOpsTest, Copy) {
unittest::TestAllTypes message, message2;
TestUtil::SetAllFields(&message);
ReflectionOps::Copy(message, &message2);
TestUtil::ExpectAllFieldsSet(message2);
// Copying from self should be a no-op.
ReflectionOps::Copy(message2, &message2);
TestUtil::ExpectAllFieldsSet(message2);
}
TEST(ReflectionOpsTest, CopyExtensions) {
unittest::TestAllExtensions message, message2;
TestUtil::SetAllExtensions(&message);
ReflectionOps::Copy(message, &message2);
TestUtil::ExpectAllExtensionsSet(message2);
}
TEST(ReflectionOpsTest, Merge) {
// Note: Copy is implemented in terms of Merge() so technically the Copy
// test already tested most of this.
unittest::TestAllTypes message, message2;
TestUtil::SetAllFields(&message);
// This field will test merging into an empty spot.
message2.set_optional_int32(message.optional_int32());
message.clear_optional_int32();
// This tests overwriting.
message2.set_optional_string(message.optional_string());
message.set_optional_string("something else");
// This tests concatenating.
message2.add_repeated_int32(message.repeated_int32(1));
int32 i = message.repeated_int32(0);
message.clear_repeated_int32();
message.add_repeated_int32(i);
ReflectionOps::Merge(message2, &message);
TestUtil::ExpectAllFieldsSet(message);
}
TEST(ReflectionOpsTest, MergeExtensions) {
// Note: Copy is implemented in terms of Merge() so technically the Copy
// test already tested most of this.
unittest::TestAllExtensions message, message2;
TestUtil::SetAllExtensions(&message);
// This field will test merging into an empty spot.
message2.SetExtension(unittest::optional_int32_extension,
message.GetExtension(unittest::optional_int32_extension));
message.ClearExtension(unittest::optional_int32_extension);
// This tests overwriting.
message2.SetExtension(unittest::optional_string_extension,
message.GetExtension(unittest::optional_string_extension));
message.SetExtension(unittest::optional_string_extension, "something else");
// This tests concatenating.
message2.AddExtension(unittest::repeated_int32_extension,
message.GetExtension(unittest::repeated_int32_extension, 1));
int32 i = message.GetExtension(unittest::repeated_int32_extension, 0);
message.ClearExtension(unittest::repeated_int32_extension);
message.AddExtension(unittest::repeated_int32_extension, i);
ReflectionOps::Merge(message2, &message);
TestUtil::ExpectAllExtensionsSet(message);
}
TEST(ReflectionOpsTest, MergeUnknown) {
// Test that the messages' UnknownFieldSets are correctly merged.
unittest::TestEmptyMessage message1, message2;
message1.mutable_unknown_fields()->AddVarint(1234, 1);
message2.mutable_unknown_fields()->AddVarint(1234, 2);
ReflectionOps::Merge(message2, &message1);
ASSERT_EQ(2, message1.unknown_fields().field_count());
ASSERT_EQ(UnknownField::TYPE_VARINT,
message1.unknown_fields().field(0).type());
EXPECT_EQ(1, message1.unknown_fields().field(0).varint());
ASSERT_EQ(UnknownField::TYPE_VARINT,
message1.unknown_fields().field(1).type());
EXPECT_EQ(2, message1.unknown_fields().field(1).varint());
}
#ifdef GTEST_HAS_DEATH_TEST
TEST(ReflectionOpsTest, MergeFromSelf) {
// Note: Copy is implemented in terms of Merge() so technically the Copy
// test already tested most of this.
unittest::TestAllTypes message;
EXPECT_DEATH(
ReflectionOps::Merge(message, &message),
"&from");
}
#endif // GTEST_HAS_DEATH_TEST
TEST(ReflectionOpsTest, Clear) {
unittest::TestAllTypes message;
TestUtil::SetAllFields(&message);
ReflectionOps::Clear(&message);
TestUtil::ExpectClear(message);
// Check that getting embedded messages returns the objects created during
// SetAllFields() rather than default instances.
EXPECT_NE(&unittest::TestAllTypes::OptionalGroup::default_instance(),
&message.optionalgroup());
EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(),
&message.optional_nested_message());
EXPECT_NE(&unittest::ForeignMessage::default_instance(),
&message.optional_foreign_message());
EXPECT_NE(&unittest_import::ImportMessage::default_instance(),
&message.optional_import_message());
}
TEST(ReflectionOpsTest, ClearExtensions) {
unittest::TestAllExtensions message;
TestUtil::SetAllExtensions(&message);
ReflectionOps::Clear(&message);
TestUtil::ExpectExtensionsClear(message);
// Check that getting embedded messages returns the objects created during
// SetAllExtensions() rather than default instances.
EXPECT_NE(&unittest::OptionalGroup_extension::default_instance(),
&message.GetExtension(unittest::optionalgroup_extension));
EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(),
&message.GetExtension(unittest::optional_nested_message_extension));
EXPECT_NE(&unittest::ForeignMessage::default_instance(),
&message.GetExtension(
unittest::optional_foreign_message_extension));
EXPECT_NE(&unittest_import::ImportMessage::default_instance(),
&message.GetExtension(unittest::optional_import_message_extension));
}
TEST(ReflectionOpsTest, ClearUnknown) {
// Test that the message's UnknownFieldSet is correctly cleared.
unittest::TestEmptyMessage message;
message.mutable_unknown_fields()->AddVarint(1234, 1);
ReflectionOps::Clear(&message);
EXPECT_EQ(0, message.unknown_fields().field_count());
}
TEST(ReflectionOpsTest, DiscardUnknownFields) {
unittest::TestAllTypes message;
TestUtil::SetAllFields(&message);
// Set some unknown fields in message.
message.mutable_unknown_fields()
->AddVarint(123456, 654321);
message.mutable_optional_nested_message()
->mutable_unknown_fields()
->AddVarint(123456, 654321);
message.mutable_repeated_nested_message(0)
->mutable_unknown_fields()
->AddVarint(123456, 654321);
EXPECT_EQ(1, message.unknown_fields().field_count());
EXPECT_EQ(1, message.optional_nested_message()
.unknown_fields().field_count());
EXPECT_EQ(1, message.repeated_nested_message(0)
.unknown_fields().field_count());
// Discard them.
ReflectionOps::DiscardUnknownFields(&message);
TestUtil::ExpectAllFieldsSet(message);
EXPECT_EQ(0, message.unknown_fields().field_count());
EXPECT_EQ(0, message.optional_nested_message()
.unknown_fields().field_count());
EXPECT_EQ(0, message.repeated_nested_message(0)
.unknown_fields().field_count());
}
TEST(ReflectionOpsTest, DiscardUnknownExtensions) {
unittest::TestAllExtensions message;
TestUtil::SetAllExtensions(&message);
// Set some unknown fields.
message.mutable_unknown_fields()
->AddVarint(123456, 654321);
message.MutableExtension(unittest::optional_nested_message_extension)
->mutable_unknown_fields()
->AddVarint(123456, 654321);
message.MutableExtension(unittest::repeated_nested_message_extension, 0)
->mutable_unknown_fields()
->AddVarint(123456, 654321);
EXPECT_EQ(1, message.unknown_fields().field_count());
EXPECT_EQ(1,
message.GetExtension(unittest::optional_nested_message_extension)
.unknown_fields().field_count());
EXPECT_EQ(1,
message.GetExtension(unittest::repeated_nested_message_extension, 0)
.unknown_fields().field_count());
// Discard them.
ReflectionOps::DiscardUnknownFields(&message);
TestUtil::ExpectAllExtensionsSet(message);
EXPECT_EQ(0, message.unknown_fields().field_count());
EXPECT_EQ(0,
message.GetExtension(unittest::optional_nested_message_extension)
.unknown_fields().field_count());
EXPECT_EQ(0,
message.GetExtension(unittest::repeated_nested_message_extension, 0)
.unknown_fields().field_count());
}
TEST(ReflectionOpsTest, IsInitialized) {
unittest::TestRequired message;
EXPECT_FALSE(ReflectionOps::IsInitialized(message));
message.set_a(1);
EXPECT_FALSE(ReflectionOps::IsInitialized(message));
message.set_b(2);
EXPECT_FALSE(ReflectionOps::IsInitialized(message));
message.set_c(3);
EXPECT_TRUE(ReflectionOps::IsInitialized(message));
}
TEST(ReflectionOpsTest, ForeignIsInitialized) {
unittest::TestRequiredForeign message;
// Starts out initialized because the foreign message is itself an optional
// field.
EXPECT_TRUE(ReflectionOps::IsInitialized(message));
// Once we create that field, the message is no longer initialized.
message.mutable_optional_message();
EXPECT_FALSE(ReflectionOps::IsInitialized(message));
// Initialize it. Now we're initialized.
message.mutable_optional_message()->set_a(1);
message.mutable_optional_message()->set_b(2);
message.mutable_optional_message()->set_c(3);
EXPECT_TRUE(ReflectionOps::IsInitialized(message));
// Add a repeated version of the message. No longer initialized.
unittest::TestRequired* sub_message = message.add_repeated_message();
EXPECT_FALSE(ReflectionOps::IsInitialized(message));
// Initialize that repeated version.
sub_message->set_a(1);
sub_message->set_b(2);
sub_message->set_c(3);
EXPECT_TRUE(ReflectionOps::IsInitialized(message));
}
TEST(ReflectionOpsTest, ExtensionIsInitialized) {
unittest::TestAllExtensions message;
// Starts out initialized because the foreign message is itself an optional
// field.
EXPECT_TRUE(ReflectionOps::IsInitialized(message));
// Once we create that field, the message is no longer initialized.
message.MutableExtension(unittest::TestRequired::single);
EXPECT_FALSE(ReflectionOps::IsInitialized(message));
// Initialize it. Now we're initialized.
message.MutableExtension(unittest::TestRequired::single)->set_a(1);
message.MutableExtension(unittest::TestRequired::single)->set_b(2);
message.MutableExtension(unittest::TestRequired::single)->set_c(3);
EXPECT_TRUE(ReflectionOps::IsInitialized(message));
// Add a repeated version of the message. No longer initialized.
message.AddExtension(unittest::TestRequired::multi);
EXPECT_FALSE(ReflectionOps::IsInitialized(message));
// Initialize that repeated version.
message.MutableExtension(unittest::TestRequired::multi, 0)->set_a(1);
message.MutableExtension(unittest::TestRequired::multi, 0)->set_b(2);
message.MutableExtension(unittest::TestRequired::multi, 0)->set_c(3);
EXPECT_TRUE(ReflectionOps::IsInitialized(message));
}
static string FindInitializationErrors(const Message& message) {
vector<string> errors;
ReflectionOps::FindInitializationErrors(message, "", &errors);
return JoinStrings(errors, ",");
}
TEST(ReflectionOpsTest, FindInitializationErrors) {
unittest::TestRequired message;
EXPECT_EQ("a,b,c", FindInitializationErrors(message));
}
TEST(ReflectionOpsTest, FindForeignInitializationErrors) {
unittest::TestRequiredForeign message;
message.mutable_optional_message();
message.add_repeated_message();
message.add_repeated_message();
EXPECT_EQ("optional_message.a,"
"optional_message.b,"
"optional_message.c,"
"repeated_message[0].a,"
"repeated_message[0].b,"
"repeated_message[0].c,"
"repeated_message[1].a,"
"repeated_message[1].b,"
"repeated_message[1].c",
FindInitializationErrors(message));
}
TEST(ReflectionOpsTest, FindExtensionInitializationErrors) {
unittest::TestAllExtensions message;
message.MutableExtension(unittest::TestRequired::single);
message.AddExtension(unittest::TestRequired::multi);
message.AddExtension(unittest::TestRequired::multi);
EXPECT_EQ("(protobuf_unittest.TestRequired.single).a,"
"(protobuf_unittest.TestRequired.single).b,"
"(protobuf_unittest.TestRequired.single).c,"
"(protobuf_unittest.TestRequired.multi)[0].a,"
"(protobuf_unittest.TestRequired.multi)[0].b,"
"(protobuf_unittest.TestRequired.multi)[0].c,"
"(protobuf_unittest.TestRequired.multi)[1].a,"
"(protobuf_unittest.TestRequired.multi)[1].b,"
"(protobuf_unittest.TestRequired.multi)[1].c",
FindInitializationErrors(message));
}
} // namespace
} // namespace internal
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,95 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
namespace internal {
void RepeatedPtrFieldBase::Reserve(int new_size) {
if (total_size_ >= new_size) return;
void** old_elements = elements_;
total_size_ = max(total_size_ * 2, new_size);
elements_ = new void*[total_size_];
memcpy(elements_, old_elements, allocated_size_ * sizeof(elements_[0]));
if (old_elements != initial_space_) {
delete [] old_elements;
}
}
void RepeatedPtrFieldBase::Swap(RepeatedPtrFieldBase* other) {
void** swap_elements = elements_;
int swap_current_size = current_size_;
int swap_allocated_size = allocated_size_;
int swap_total_size = total_size_;
// We may not be using initial_space_ but it's not worth checking. Just
// copy it anyway.
void* swap_initial_space[kInitialSize];
memcpy(swap_initial_space, initial_space_, sizeof(initial_space_));
elements_ = other->elements_;
current_size_ = other->current_size_;
allocated_size_ = other->allocated_size_;
total_size_ = other->total_size_;
memcpy(initial_space_, other->initial_space_, sizeof(initial_space_));
other->elements_ = swap_elements;
other->current_size_ = swap_current_size;
other->allocated_size_ = swap_allocated_size;
other->total_size_ = swap_total_size;
memcpy(other->initial_space_, swap_initial_space, sizeof(swap_initial_space));
if (elements_ == other->initial_space_) {
elements_ = initial_space_;
}
if (other->elements_ == initial_space_) {
other->elements_ = other->initial_space_;
}
}
string* StringTypeHandlerBase::New() {
return new string;
}
void StringTypeHandlerBase::Delete(string* value) {
delete value;
}
} // namespace internal
} // namespace protobuf
} // namespace google

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,986 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// TODO(kenton): Improve this unittest to bring it up to the standards of
// other proto2 unittests.
#include <algorithm>
#include <list>
#include <vector>
#include <google/protobuf/repeated_field.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
using protobuf_unittest::TestAllTypes;
namespace protobuf {
namespace {
// Test operations on a RepeatedField which is small enough that it does
// not allocate a separate array for storage.
TEST(RepeatedField, Small) {
RepeatedField<int> field;
EXPECT_EQ(field.size(), 0);
field.Add(5);
EXPECT_EQ(field.size(), 1);
EXPECT_EQ(field.Get(0), 5);
field.Add(42);
EXPECT_EQ(field.size(), 2);
EXPECT_EQ(field.Get(0), 5);
EXPECT_EQ(field.Get(1), 42);
field.Set(1, 23);
EXPECT_EQ(field.size(), 2);
EXPECT_EQ(field.Get(0), 5);
EXPECT_EQ(field.Get(1), 23);
EXPECT_EQ(field.SpaceUsedExcludingSelf(), 0);
field.RemoveLast();
EXPECT_EQ(field.size(), 1);
EXPECT_EQ(field.Get(0), 5);
field.Clear();
EXPECT_EQ(field.size(), 0);
EXPECT_EQ(field.SpaceUsedExcludingSelf(), 0);
}
// Test operations on a RepeatedField which is large enough to allocate a
// separate array.
TEST(RepeatedField, Large) {
RepeatedField<int> field;
for (int i = 0; i < 16; i++) {
field.Add(i * i);
}
EXPECT_EQ(field.size(), 16);
for (int i = 0; i < 16; i++) {
EXPECT_EQ(field.Get(i), i * i);
}
int expected_usage = 16 * sizeof(int);
EXPECT_GE(field.SpaceUsedExcludingSelf(), expected_usage);
}
// Test swapping between various types of RepeatedFields.
TEST(RepeatedField, SwapSmallSmall) {
RepeatedField<int> field1;
RepeatedField<int> field2;
field1.Add(5);
field1.Add(42);
field1.Swap(&field2);
EXPECT_EQ(field1.size(), 0);
EXPECT_EQ(field2.size(), 2);
EXPECT_EQ(field2.Get(0), 5);
EXPECT_EQ(field2.Get(1), 42);
}
TEST(RepeatedField, SwapLargeSmall) {
RepeatedField<int> field1;
RepeatedField<int> field2;
for (int i = 0; i < 16; i++) {
field1.Add(i * i);
}
field2.Add(5);
field2.Add(42);
field1.Swap(&field2);
EXPECT_EQ(field1.size(), 2);
EXPECT_EQ(field1.Get(0), 5);
EXPECT_EQ(field1.Get(1), 42);
EXPECT_EQ(field2.size(), 16);
for (int i = 0; i < 16; i++) {
EXPECT_EQ(field2.Get(i), i * i);
}
}
TEST(RepeatedField, SwapLargeLarge) {
RepeatedField<int> field1;
RepeatedField<int> field2;
field1.Add(5);
field1.Add(42);
for (int i = 0; i < 16; i++) {
field1.Add(i);
field2.Add(i * i);
}
field2.Swap(&field1);
EXPECT_EQ(field1.size(), 16);
for (int i = 0; i < 16; i++) {
EXPECT_EQ(field1.Get(i), i * i);
}
EXPECT_EQ(field2.size(), 18);
EXPECT_EQ(field2.Get(0), 5);
EXPECT_EQ(field2.Get(1), 42);
for (int i = 2; i < 18; i++) {
EXPECT_EQ(field2.Get(i), i - 2);
}
}
// Determines how much space was reserved by the given field by adding elements
// to it until it re-allocates its space.
static int ReservedSpace(RepeatedField<int>* field) {
const int* ptr = field->data();
do {
field->Add(0);
} while (field->data() == ptr);
return field->size() - 1;
}
TEST(RepeatedField, ReserveMoreThanDouble) {
// Reserve more than double the previous space in the field and expect the
// field to reserve exactly the amount specified.
RepeatedField<int> field;
field.Reserve(20);
EXPECT_EQ(20, ReservedSpace(&field));
}
TEST(RepeatedField, ReserveLessThanDouble) {
// Reserve less than double the previous space in the field and expect the
// field to grow by double instead.
RepeatedField<int> field;
field.Reserve(20);
field.Reserve(30);
EXPECT_EQ(40, ReservedSpace(&field));
}
TEST(RepeatedField, ReserveLessThanExisting) {
// Reserve less than the previous space in the field and expect the
// field to not re-allocate at all.
RepeatedField<int> field;
field.Reserve(20);
const int* previous_ptr = field.data();
field.Reserve(10);
EXPECT_EQ(previous_ptr, field.data());
EXPECT_EQ(20, ReservedSpace(&field));
}
TEST(RepeatedField, MergeFrom) {
RepeatedField<int> source, destination;
source.Add(4);
source.Add(5);
destination.Add(1);
destination.Add(2);
destination.Add(3);
destination.MergeFrom(source);
ASSERT_EQ(5, destination.size());
EXPECT_EQ(1, destination.Get(0));
EXPECT_EQ(2, destination.Get(1));
EXPECT_EQ(3, destination.Get(2));
EXPECT_EQ(4, destination.Get(3));
EXPECT_EQ(5, destination.Get(4));
}
TEST(RepeatedField, MutableDataIsMutable) {
RepeatedField<int> field;
field.Add(1);
EXPECT_EQ(1, field.Get(0));
// The fact that this line compiles would be enough, but we'll check the
// value anyway.
*field.mutable_data() = 2;
EXPECT_EQ(2, field.Get(0));
}
TEST(RepeatedField, Truncate) {
RepeatedField<int> field;
field.Add(12);
field.Add(34);
field.Add(56);
field.Add(78);
EXPECT_EQ(4, field.size());
field.Truncate(3);
EXPECT_EQ(3, field.size());
field.Add(90);
EXPECT_EQ(4, field.size());
EXPECT_EQ(90, field.Get(3));
// Truncations that don't change the size are allowed, but growing is not
// allowed.
field.Truncate(field.size());
#ifdef GTEST_HAS_DEATH_TEST
EXPECT_DEBUG_DEATH(field.Truncate(field.size() + 1), "new_size");
#endif
}
// ===================================================================
// RepeatedPtrField tests. These pretty much just mirror the RepeatedField
// tests above.
TEST(RepeatedPtrField, Small) {
RepeatedPtrField<string> field;
EXPECT_EQ(field.size(), 0);
field.Add()->assign("foo");
EXPECT_EQ(field.size(), 1);
EXPECT_EQ(field.Get(0), "foo");
field.Add()->assign("bar");
EXPECT_EQ(field.size(), 2);
EXPECT_EQ(field.Get(0), "foo");
EXPECT_EQ(field.Get(1), "bar");
field.Mutable(1)->assign("baz");
EXPECT_EQ(field.size(), 2);
EXPECT_EQ(field.Get(0), "foo");
EXPECT_EQ(field.Get(1), "baz");
field.RemoveLast();
EXPECT_EQ(field.size(), 1);
EXPECT_EQ(field.Get(0), "foo");
field.Clear();
EXPECT_EQ(field.size(), 0);
}
TEST(RepeatedPtrField, Large) {
RepeatedPtrField<string> field;
for (int i = 0; i < 16; i++) {
*field.Add() += 'a' + i;
}
EXPECT_EQ(field.size(), 16);
for (int i = 0; i < 16; i++) {
EXPECT_EQ(field.Get(i).size(), 1);
EXPECT_EQ(field.Get(i)[0], 'a' + i);
}
int min_expected_usage = 16 * sizeof(string);
EXPECT_GE(field.SpaceUsedExcludingSelf(), min_expected_usage);
}
TEST(RepeatedPtrField, SwapSmallSmall) {
RepeatedPtrField<string> field1;
RepeatedPtrField<string> field2;
field1.Add()->assign("foo");
field1.Add()->assign("bar");
field1.Swap(&field2);
EXPECT_EQ(field1.size(), 0);
EXPECT_EQ(field2.size(), 2);
EXPECT_EQ(field2.Get(0), "foo");
EXPECT_EQ(field2.Get(1), "bar");
}
TEST(RepeatedPtrField, SwapLargeSmall) {
RepeatedPtrField<string> field1;
RepeatedPtrField<string> field2;
field2.Add()->assign("foo");
field2.Add()->assign("bar");
for (int i = 0; i < 16; i++) {
*field1.Add() += 'a' + i;
}
field1.Swap(&field2);
EXPECT_EQ(field1.size(), 2);
EXPECT_EQ(field1.Get(0), "foo");
EXPECT_EQ(field1.Get(1), "bar");
EXPECT_EQ(field2.size(), 16);
for (int i = 0; i < 16; i++) {
EXPECT_EQ(field2.Get(i).size(), 1);
EXPECT_EQ(field2.Get(i)[0], 'a' + i);
}
}
TEST(RepeatedPtrField, SwapLargeLarge) {
RepeatedPtrField<string> field1;
RepeatedPtrField<string> field2;
field1.Add()->assign("foo");
field1.Add()->assign("bar");
for (int i = 0; i < 16; i++) {
*field1.Add() += 'A' + i;
*field2.Add() += 'a' + i;
}
field2.Swap(&field1);
EXPECT_EQ(field1.size(), 16);
for (int i = 0; i < 16; i++) {
EXPECT_EQ(field1.Get(i).size(), 1);
EXPECT_EQ(field1.Get(i)[0], 'a' + i);
}
EXPECT_EQ(field2.size(), 18);
EXPECT_EQ(field2.Get(0), "foo");
EXPECT_EQ(field2.Get(1), "bar");
for (int i = 2; i < 18; i++) {
EXPECT_EQ(field2.Get(i).size(), 1);
EXPECT_EQ(field2.Get(i)[0], 'A' + i - 2);
}
}
static int ReservedSpace(RepeatedPtrField<string>* field) {
const string* const* ptr = field->data();
do {
field->Add();
} while (field->data() == ptr);
return field->size() - 1;
}
TEST(RepeatedPtrField, ReserveMoreThanDouble) {
RepeatedPtrField<string> field;
field.Reserve(20);
EXPECT_EQ(20, ReservedSpace(&field));
}
TEST(RepeatedPtrField, ReserveLessThanDouble) {
RepeatedPtrField<string> field;
field.Reserve(20);
field.Reserve(30);
EXPECT_EQ(40, ReservedSpace(&field));
}
TEST(RepeatedPtrField, ReserveLessThanExisting) {
RepeatedPtrField<string> field;
field.Reserve(20);
const string* const* previous_ptr = field.data();
field.Reserve(10);
EXPECT_EQ(previous_ptr, field.data());
EXPECT_EQ(20, ReservedSpace(&field));
}
TEST(RepeatedPtrField, ReserveDoesntLoseAllocated) {
// Check that a bug is fixed: An earlier implementation of Reserve()
// failed to copy pointers to allocated-but-cleared objects, possibly
// leading to segfaults.
RepeatedPtrField<string> field;
string* first = field.Add();
field.RemoveLast();
field.Reserve(20);
EXPECT_EQ(first, field.Add());
}
// Clearing elements is tricky with RepeatedPtrFields since the memory for
// the elements is retained and reused.
TEST(RepeatedPtrField, ClearedElements) {
RepeatedPtrField<string> field;
string* original = field.Add();
*original = "foo";
EXPECT_EQ(field.ClearedCount(), 0);
field.RemoveLast();
EXPECT_TRUE(original->empty());
EXPECT_EQ(field.ClearedCount(), 1);
EXPECT_EQ(field.Add(), original); // Should return same string for reuse.
EXPECT_EQ(field.ReleaseLast(), original); // We take ownership.
EXPECT_EQ(field.ClearedCount(), 0);
EXPECT_NE(field.Add(), original); // Should NOT return the same string.
EXPECT_EQ(field.ClearedCount(), 0);
field.AddAllocated(original); // Give ownership back.
EXPECT_EQ(field.ClearedCount(), 0);
EXPECT_EQ(field.Mutable(1), original);
field.Clear();
EXPECT_EQ(field.ClearedCount(), 2);
EXPECT_EQ(field.ReleaseCleared(), original); // Take ownership again.
EXPECT_EQ(field.ClearedCount(), 1);
EXPECT_NE(field.Add(), original);
EXPECT_EQ(field.ClearedCount(), 0);
EXPECT_NE(field.Add(), original);
EXPECT_EQ(field.ClearedCount(), 0);
field.AddCleared(original); // Give ownership back, but as a cleared object.
EXPECT_EQ(field.ClearedCount(), 1);
EXPECT_EQ(field.Add(), original);
EXPECT_EQ(field.ClearedCount(), 0);
}
// Test all code paths in AddAllocated().
TEST(RepeatedPtrField, AddAlocated) {
RepeatedPtrField<string> field;
while (field.size() < field.Capacity()) {
field.Add()->assign("filler");
}
int index = field.size();
// First branch: Field is at capacity with no cleared objects.
string* foo = new string("foo");
field.AddAllocated(foo);
EXPECT_EQ(index + 1, field.size());
EXPECT_EQ(0, field.ClearedCount());
EXPECT_EQ(foo, &field.Get(index));
// Last branch: Field is not at capacity and there are no cleared objects.
string* bar = new string("bar");
field.AddAllocated(bar);
++index;
EXPECT_EQ(index + 1, field.size());
EXPECT_EQ(0, field.ClearedCount());
EXPECT_EQ(bar, &field.Get(index));
// Third branch: Field is not at capacity and there are no cleared objects.
field.RemoveLast();
string* baz = new string("baz");
field.AddAllocated(baz);
EXPECT_EQ(index + 1, field.size());
EXPECT_EQ(1, field.ClearedCount());
EXPECT_EQ(baz, &field.Get(index));
// Second branch: Field is at capacity but has some cleared objects.
while (field.size() < field.Capacity()) {
field.Add()->assign("filler2");
}
field.RemoveLast();
index = field.size();
string* qux = new string("qux");
field.AddAllocated(qux);
EXPECT_EQ(index + 1, field.size());
// We should have discarded the cleared object.
EXPECT_EQ(0, field.ClearedCount());
EXPECT_EQ(qux, &field.Get(index));
}
TEST(RepeatedPtrField, MergeFrom) {
RepeatedPtrField<string> source, destination;
source.Add()->assign("4");
source.Add()->assign("5");
destination.Add()->assign("1");
destination.Add()->assign("2");
destination.Add()->assign("3");
destination.MergeFrom(source);
ASSERT_EQ(5, destination.size());
EXPECT_EQ("1", destination.Get(0));
EXPECT_EQ("2", destination.Get(1));
EXPECT_EQ("3", destination.Get(2));
EXPECT_EQ("4", destination.Get(3));
EXPECT_EQ("5", destination.Get(4));
}
TEST(RepeatedPtrField, MutableDataIsMutable) {
RepeatedPtrField<string> field;
*field.Add() = "1";
EXPECT_EQ("1", field.Get(0));
// The fact that this line compiles would be enough, but we'll check the
// value anyway.
string** data = field.mutable_data();
**data = "2";
EXPECT_EQ("2", field.Get(0));
}
// ===================================================================
// Iterator tests stolen from net/proto/proto-array_unittest.
class RepeatedFieldIteratorTest : public testing::Test {
protected:
virtual void SetUp() {
for (int i = 0; i < 3; ++i) {
proto_array_.Add(i);
}
}
RepeatedField<int> proto_array_;
};
TEST_F(RepeatedFieldIteratorTest, Convertible) {
RepeatedField<int>::iterator iter = proto_array_.begin();
RepeatedField<int>::const_iterator c_iter = iter;
EXPECT_EQ(0, *c_iter);
}
TEST_F(RepeatedFieldIteratorTest, MutableIteration) {
RepeatedField<int>::iterator iter = proto_array_.begin();
EXPECT_EQ(0, *iter);
++iter;
EXPECT_EQ(1, *iter++);
EXPECT_EQ(2, *iter);
++iter;
EXPECT_TRUE(proto_array_.end() == iter);
EXPECT_EQ(2, *(proto_array_.end() - 1));
}
TEST_F(RepeatedFieldIteratorTest, ConstIteration) {
const RepeatedField<int>& const_proto_array = proto_array_;
RepeatedField<int>::const_iterator iter = const_proto_array.begin();
EXPECT_EQ(0, *iter);
++iter;
EXPECT_EQ(1, *iter++);
EXPECT_EQ(2, *iter);
++iter;
EXPECT_TRUE(proto_array_.end() == iter);
EXPECT_EQ(2, *(proto_array_.end() - 1));
}
TEST_F(RepeatedFieldIteratorTest, Mutation) {
RepeatedField<int>::iterator iter = proto_array_.begin();
*iter = 7;
EXPECT_EQ(7, proto_array_.Get(0));
}
// -------------------------------------------------------------------
class RepeatedPtrFieldIteratorTest : public testing::Test {
protected:
virtual void SetUp() {
proto_array_.Add()->assign("foo");
proto_array_.Add()->assign("bar");
proto_array_.Add()->assign("baz");
}
RepeatedPtrField<string> proto_array_;
};
TEST_F(RepeatedPtrFieldIteratorTest, Convertible) {
RepeatedPtrField<string>::iterator iter = proto_array_.begin();
RepeatedPtrField<string>::const_iterator c_iter = iter;
}
TEST_F(RepeatedPtrFieldIteratorTest, MutableIteration) {
RepeatedPtrField<string>::iterator iter = proto_array_.begin();
EXPECT_EQ("foo", *iter);
++iter;
EXPECT_EQ("bar", *(iter++));
EXPECT_EQ("baz", *iter);
++iter;
EXPECT_TRUE(proto_array_.end() == iter);
EXPECT_EQ("baz", *(--proto_array_.end()));
}
TEST_F(RepeatedPtrFieldIteratorTest, ConstIteration) {
const RepeatedPtrField<string>& const_proto_array = proto_array_;
RepeatedPtrField<string>::const_iterator iter = const_proto_array.begin();
EXPECT_EQ("foo", *iter);
++iter;
EXPECT_EQ("bar", *(iter++));
EXPECT_EQ("baz", *iter);
++iter;
EXPECT_TRUE(const_proto_array.end() == iter);
EXPECT_EQ("baz", *(--const_proto_array.end()));
}
TEST_F(RepeatedPtrFieldIteratorTest, RandomAccess) {
RepeatedPtrField<string>::iterator iter = proto_array_.begin();
RepeatedPtrField<string>::iterator iter2 = iter;
++iter2;
++iter2;
EXPECT_TRUE(iter + 2 == iter2);
EXPECT_TRUE(iter == iter2 - 2);
EXPECT_EQ("baz", iter[2]);
EXPECT_EQ("baz", *(iter + 2));
EXPECT_EQ(3, proto_array_.end() - proto_array_.begin());
}
TEST_F(RepeatedPtrFieldIteratorTest, Comparable) {
RepeatedPtrField<string>::const_iterator iter = proto_array_.begin();
RepeatedPtrField<string>::const_iterator iter2 = iter + 1;
EXPECT_TRUE(iter == iter);
EXPECT_TRUE(iter != iter2);
EXPECT_TRUE(iter < iter2);
EXPECT_TRUE(iter <= iter2);
EXPECT_TRUE(iter <= iter);
EXPECT_TRUE(iter2 > iter);
EXPECT_TRUE(iter2 >= iter);
EXPECT_TRUE(iter >= iter);
}
// Uninitialized iterator does not point to any of the RepeatedPtrField.
TEST_F(RepeatedPtrFieldIteratorTest, UninitializedIterator) {
RepeatedPtrField<string>::iterator iter;
EXPECT_TRUE(iter != proto_array_.begin());
EXPECT_TRUE(iter != proto_array_.begin() + 1);
EXPECT_TRUE(iter != proto_array_.begin() + 2);
EXPECT_TRUE(iter != proto_array_.begin() + 3);
EXPECT_TRUE(iter != proto_array_.end());
}
TEST_F(RepeatedPtrFieldIteratorTest, STLAlgorithms_lower_bound) {
proto_array_.Clear();
proto_array_.Add()->assign("a");
proto_array_.Add()->assign("c");
proto_array_.Add()->assign("d");
proto_array_.Add()->assign("n");
proto_array_.Add()->assign("p");
proto_array_.Add()->assign("x");
proto_array_.Add()->assign("y");
string v = "f";
RepeatedPtrField<string>::const_iterator it =
lower_bound(proto_array_.begin(), proto_array_.end(), v);
EXPECT_EQ(*it, "n");
EXPECT_TRUE(it == proto_array_.begin() + 3);
}
TEST_F(RepeatedPtrFieldIteratorTest, Mutation) {
RepeatedPtrField<string>::iterator iter = proto_array_.begin();
*iter = "qux";
EXPECT_EQ("qux", proto_array_.Get(0));
}
// -------------------------------------------------------------------
class RepeatedPtrFieldPtrsIteratorTest : public testing::Test {
protected:
virtual void SetUp() {
proto_array_.Add()->assign("foo");
proto_array_.Add()->assign("bar");
proto_array_.Add()->assign("baz");
}
RepeatedPtrField<string> proto_array_;
};
TEST_F(RepeatedPtrFieldPtrsIteratorTest, ConvertiblePtr) {
RepeatedPtrField<string>::pointer_iterator iter =
proto_array_.pointer_begin();
}
TEST_F(RepeatedPtrFieldPtrsIteratorTest, MutablePtrIteration) {
RepeatedPtrField<string>::pointer_iterator iter =
proto_array_.pointer_begin();
EXPECT_EQ("foo", **iter);
++iter;
EXPECT_EQ("bar", **(iter++));
EXPECT_EQ("baz", **iter);
++iter;
EXPECT_TRUE(proto_array_.pointer_end() == iter);
EXPECT_EQ("baz", **(--proto_array_.pointer_end()));
}
TEST_F(RepeatedPtrFieldPtrsIteratorTest, RandomPtrAccess) {
RepeatedPtrField<string>::pointer_iterator iter =
proto_array_.pointer_begin();
RepeatedPtrField<string>::pointer_iterator iter2 = iter;
++iter2;
++iter2;
EXPECT_TRUE(iter + 2 == iter2);
EXPECT_TRUE(iter == iter2 - 2);
EXPECT_EQ("baz", *iter[2]);
EXPECT_EQ("baz", **(iter + 2));
EXPECT_EQ(3, proto_array_.end() - proto_array_.begin());
}
TEST_F(RepeatedPtrFieldPtrsIteratorTest, ComparablePtr) {
RepeatedPtrField<string>::pointer_iterator iter =
proto_array_.pointer_begin();
RepeatedPtrField<string>::pointer_iterator iter2 = iter + 1;
EXPECT_TRUE(iter == iter);
EXPECT_TRUE(iter != iter2);
EXPECT_TRUE(iter < iter2);
EXPECT_TRUE(iter <= iter2);
EXPECT_TRUE(iter <= iter);
EXPECT_TRUE(iter2 > iter);
EXPECT_TRUE(iter2 >= iter);
EXPECT_TRUE(iter >= iter);
}
// Uninitialized iterator does not point to any of the RepeatedPtrOverPtrs.
// Dereferencing an uninitialized iterator crashes the process.
TEST_F(RepeatedPtrFieldPtrsIteratorTest, UninitializedPtrIterator) {
RepeatedPtrField<string>::pointer_iterator iter;
EXPECT_TRUE(iter != proto_array_.pointer_begin());
EXPECT_TRUE(iter != proto_array_.pointer_begin() + 1);
EXPECT_TRUE(iter != proto_array_.pointer_begin() + 2);
EXPECT_TRUE(iter != proto_array_.pointer_begin() + 3);
EXPECT_TRUE(iter != proto_array_.pointer_end());
}
// This comparison functor is required by the tests for RepeatedPtrOverPtrs.
// They operate on strings and need to compare strings as strings in
// any stl algorithm, even though the iterator returns a pointer to a string
// - i.e. *iter has type string*.
struct StringLessThan {
bool operator()(const string* z, const string& y) {
return *z < y;
}
bool operator()(const string* z, const string* y) {
return *z < *y;
}
};
TEST_F(RepeatedPtrFieldPtrsIteratorTest, PtrSTLAlgorithms_lower_bound) {
proto_array_.Clear();
proto_array_.Add()->assign("a");
proto_array_.Add()->assign("c");
proto_array_.Add()->assign("d");
proto_array_.Add()->assign("n");
proto_array_.Add()->assign("p");
proto_array_.Add()->assign("x");
proto_array_.Add()->assign("y");
RepeatedPtrField<string>::pointer_iterator iter =
proto_array_.pointer_begin();
string v = "f";
RepeatedPtrField<string>::pointer_iterator it =
lower_bound(proto_array_.pointer_begin(), proto_array_.pointer_end(),
&v, StringLessThan());
GOOGLE_CHECK(*it != NULL);
EXPECT_EQ(**it, "n");
EXPECT_TRUE(it == proto_array_.pointer_begin() + 3);
}
TEST_F(RepeatedPtrFieldPtrsIteratorTest, PtrMutation) {
RepeatedPtrField<string>::pointer_iterator iter =
proto_array_.pointer_begin();
**iter = "qux";
EXPECT_EQ("qux", proto_array_.Get(0));
EXPECT_EQ("bar", proto_array_.Get(1));
EXPECT_EQ("baz", proto_array_.Get(2));
++iter;
delete *iter;
*iter = new string("a");
++iter;
delete *iter;
*iter = new string("b");
EXPECT_EQ("a", proto_array_.Get(1));
EXPECT_EQ("b", proto_array_.Get(2));
}
TEST_F(RepeatedPtrFieldPtrsIteratorTest, Sort) {
proto_array_.Add()->assign("c");
proto_array_.Add()->assign("d");
proto_array_.Add()->assign("n");
proto_array_.Add()->assign("p");
proto_array_.Add()->assign("a");
proto_array_.Add()->assign("y");
proto_array_.Add()->assign("x");
EXPECT_EQ("foo", proto_array_.Get(0));
EXPECT_EQ("n", proto_array_.Get(5));
EXPECT_EQ("x", proto_array_.Get(9));
sort(proto_array_.pointer_begin(),
proto_array_.pointer_end(),
StringLessThan());
EXPECT_EQ("a", proto_array_.Get(0));
EXPECT_EQ("baz", proto_array_.Get(2));
EXPECT_EQ("y", proto_array_.Get(9));
}
// -----------------------------------------------------------------------------
// Unit-tests for the insert iterators
// google::protobuf::RepeatedFieldBackInserter,
// google::protobuf::AllocatedRepeatedPtrFieldBackInserter
// Ported from util/gtl/proto-array-iterators_unittest.
class RepeatedFieldInsertionIteratorsTest : public testing::Test {
protected:
std::list<double> halves;
std::list<int> fibonacci;
std::vector<string> words;
typedef TestAllTypes::NestedMessage Nested;
Nested nesteds[2];
std::vector<Nested*> nested_ptrs;
TestAllTypes protobuffer;
virtual void SetUp() {
fibonacci.push_back(1);
fibonacci.push_back(1);
fibonacci.push_back(2);
fibonacci.push_back(3);
fibonacci.push_back(5);
fibonacci.push_back(8);
std::copy(fibonacci.begin(), fibonacci.end(),
RepeatedFieldBackInserter(protobuffer.mutable_repeated_int32()));
halves.push_back(1.0);
halves.push_back(0.5);
halves.push_back(0.25);
halves.push_back(0.125);
halves.push_back(0.0625);
std::copy(halves.begin(), halves.end(),
RepeatedFieldBackInserter(protobuffer.mutable_repeated_double()));
words.push_back("Able");
words.push_back("was");
words.push_back("I");
words.push_back("ere");
words.push_back("I");
words.push_back("saw");
words.push_back("Elba");
std::copy(words.begin(), words.end(),
RepeatedFieldBackInserter(protobuffer.mutable_repeated_string()));
nesteds[0].set_bb(17);
nesteds[1].set_bb(4711);
std::copy(&nesteds[0], &nesteds[2],
RepeatedFieldBackInserter(
protobuffer.mutable_repeated_nested_message()));
nested_ptrs.push_back(new Nested);
nested_ptrs.back()->set_bb(170);
nested_ptrs.push_back(new Nested);
nested_ptrs.back()->set_bb(47110);
std::copy(nested_ptrs.begin(), nested_ptrs.end(),
RepeatedFieldBackInserter(
protobuffer.mutable_repeated_nested_message()));
}
virtual void TearDown() {
STLDeleteContainerPointers(nested_ptrs.begin(), nested_ptrs.end());
}
};
TEST_F(RepeatedFieldInsertionIteratorsTest, Fibonacci) {
EXPECT_TRUE(std::equal(fibonacci.begin(),
fibonacci.end(),
protobuffer.repeated_int32().begin()));
EXPECT_TRUE(std::equal(protobuffer.repeated_int32().begin(),
protobuffer.repeated_int32().end(),
fibonacci.begin()));
}
TEST_F(RepeatedFieldInsertionIteratorsTest, Halves) {
EXPECT_TRUE(std::equal(halves.begin(),
halves.end(),
protobuffer.repeated_double().begin()));
EXPECT_TRUE(std::equal(protobuffer.repeated_double().begin(),
protobuffer.repeated_double().end(),
halves.begin()));
}
TEST_F(RepeatedFieldInsertionIteratorsTest, Words) {
ASSERT_EQ(words.size(), protobuffer.repeated_string_size());
EXPECT_EQ(words.at(0), protobuffer.repeated_string(0));
EXPECT_EQ(words.at(1), protobuffer.repeated_string(1));
EXPECT_EQ(words.at(2), protobuffer.repeated_string(2));
EXPECT_EQ(words.at(3), protobuffer.repeated_string(3));
EXPECT_EQ(words.at(4), protobuffer.repeated_string(4));
EXPECT_EQ(words.at(5), protobuffer.repeated_string(5));
EXPECT_EQ(words.at(6), protobuffer.repeated_string(6));
}
TEST_F(RepeatedFieldInsertionIteratorsTest, Nesteds) {
ASSERT_EQ(protobuffer.repeated_nested_message_size(), 4);
EXPECT_EQ(protobuffer.repeated_nested_message(0).bb(), 17);
EXPECT_EQ(protobuffer.repeated_nested_message(1).bb(), 4711);
EXPECT_EQ(protobuffer.repeated_nested_message(2).bb(), 170);
EXPECT_EQ(protobuffer.repeated_nested_message(3).bb(), 47110);
}
TEST_F(RepeatedFieldInsertionIteratorsTest,
AllocatedRepeatedPtrFieldWithStringIntData) {
vector<Nested*> data;
TestAllTypes goldenproto;
for (int i = 0; i < 10; ++i) {
Nested* new_data = new Nested;
new_data->set_bb(i);
data.push_back(new_data);
new_data = goldenproto.add_repeated_nested_message();
new_data->set_bb(i);
}
TestAllTypes testproto;
copy(data.begin(), data.end(),
AllocatedRepeatedPtrFieldBackInserter(
testproto.mutable_repeated_nested_message()));
EXPECT_EQ(testproto.DebugString(), goldenproto.DebugString());
}
TEST_F(RepeatedFieldInsertionIteratorsTest,
AllocatedRepeatedPtrFieldWithString) {
vector<string*> data;
TestAllTypes goldenproto;
for (int i = 0; i < 10; ++i) {
string* new_data = new string;
*new_data = "name-" + SimpleItoa(i);
data.push_back(new_data);
new_data = goldenproto.add_repeated_string();
*new_data = "name-" + SimpleItoa(i);
}
TestAllTypes testproto;
copy(data.begin(), data.end(),
AllocatedRepeatedPtrFieldBackInserter(
testproto.mutable_repeated_string()));
EXPECT_EQ(testproto.DebugString(), goldenproto.DebugString());
}
} // namespace
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,46 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/service.h>
namespace google {
namespace protobuf {
Service::~Service() {}
RpcChannel::~RpcChannel() {}
RpcController::~RpcController() {}
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,291 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// DEPRECATED: This module declares the abstract interfaces underlying proto2
// RPC services. These are intented to be independent of any particular RPC
// implementation, so that proto2 services can be used on top of a variety
// of implementations. Starting with version 2.3.0, RPC implementations should
// not try to build on these, but should instead provide code generator plugins
// which generate code specific to the particular RPC implementation. This way
// the generated code can be more appropriate for the implementation in use
// and can avoid unnecessary layers of indirection.
//
//
// When you use the protocol compiler to compile a service definition, it
// generates two classes: An abstract interface for the service (with
// methods matching the service definition) and a "stub" implementation.
// A stub is just a type-safe wrapper around an RpcChannel which emulates a
// local implementation of the service.
//
// For example, the service definition:
// service MyService {
// rpc Foo(MyRequest) returns(MyResponse);
// }
// will generate abstract interface "MyService" and class "MyService::Stub".
// You could implement a MyService as follows:
// class MyServiceImpl : public MyService {
// public:
// MyServiceImpl() {}
// ~MyServiceImpl() {}
//
// // implements MyService ---------------------------------------
//
// void Foo(google::protobuf::RpcController* controller,
// const MyRequest* request,
// MyResponse* response,
// Closure* done) {
// // ... read request and fill in response ...
// done->Run();
// }
// };
// You would then register an instance of MyServiceImpl with your RPC server
// implementation. (How to do that depends on the implementation.)
//
// To call a remote MyServiceImpl, first you need an RpcChannel connected to it.
// How to construct a channel depends, again, on your RPC implementation.
// Here we use a hypothentical "MyRpcChannel" as an example:
// MyRpcChannel channel("rpc:hostname:1234/myservice");
// MyRpcController controller;
// MyServiceImpl::Stub stub(&channel);
// FooRequest request;
// FooRespnose response;
//
// // ... fill in request ...
//
// stub.Foo(&controller, request, &response, NewCallback(HandleResponse));
//
// On Thread-Safety:
//
// Different RPC implementations may make different guarantees about what
// threads they may run callbacks on, and what threads the application is
// allowed to use to call the RPC system. Portable software should be ready
// for callbacks to be called on any thread, but should not try to call the
// RPC system from any thread except for the ones on which it received the
// callbacks. Realistically, though, simple software will probably want to
// use a single-threaded RPC system while high-end software will want to
// use multiple threads. RPC implementations should provide multiple
// choices.
#ifndef GOOGLE_PROTOBUF_SERVICE_H__
#define GOOGLE_PROTOBUF_SERVICE_H__
#include <string>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
// Defined in this file.
class Service;
class RpcController;
class RpcChannel;
// Defined in other files.
class Descriptor; // descriptor.h
class ServiceDescriptor; // descriptor.h
class MethodDescriptor; // descriptor.h
class Message; // message.h
// Abstract base interface for protocol-buffer-based RPC services. Services
// themselves are abstract interfaces (implemented either by servers or as
// stubs), but they subclass this base interface. The methods of this
// interface can be used to call the methods of the Service without knowing
// its exact type at compile time (analogous to Reflection).
class LIBPROTOBUF_EXPORT Service {
public:
inline Service() {}
virtual ~Service();
// When constructing a stub, you may pass STUB_OWNS_CHANNEL as the second
// parameter to the constructor to tell it to delete its RpcChannel when
// destroyed.
enum ChannelOwnership {
STUB_OWNS_CHANNEL,
STUB_DOESNT_OWN_CHANNEL
};
// Get the ServiceDescriptor describing this service and its methods.
virtual const ServiceDescriptor* GetDescriptor() = 0;
// Call a method of the service specified by MethodDescriptor. This is
// normally implemented as a simple switch() that calls the standard
// definitions of the service's methods.
//
// Preconditions:
// * method->service() == GetDescriptor()
// * request and response are of the exact same classes as the objects
// returned by GetRequestPrototype(method) and
// GetResponsePrototype(method).
// * After the call has started, the request must not be modified and the
// response must not be accessed at all until "done" is called.
// * "controller" is of the correct type for the RPC implementation being
// used by this Service. For stubs, the "correct type" depends on the
// RpcChannel which the stub is using. Server-side Service
// implementations are expected to accept whatever type of RpcController
// the server-side RPC implementation uses.
//
// Postconditions:
// * "done" will be called when the method is complete. This may be
// before CallMethod() returns or it may be at some point in the future.
// * If the RPC succeeded, "response" contains the response returned by
// the server.
// * If the RPC failed, "response"'s contents are undefined. The
// RpcController can be queried to determine if an error occurred and
// possibly to get more information about the error.
virtual void CallMethod(const MethodDescriptor* method,
RpcController* controller,
const Message* request,
Message* response,
Closure* done) = 0;
// CallMethod() requires that the request and response passed in are of a
// particular subclass of Message. GetRequestPrototype() and
// GetResponsePrototype() get the default instances of these required types.
// You can then call Message::New() on these instances to construct mutable
// objects which you can then pass to CallMethod().
//
// Example:
// const MethodDescriptor* method =
// service->GetDescriptor()->FindMethodByName("Foo");
// Message* request = stub->GetRequestPrototype (method)->New();
// Message* response = stub->GetResponsePrototype(method)->New();
// request->ParseFromString(input);
// service->CallMethod(method, *request, response, callback);
virtual const Message& GetRequestPrototype(
const MethodDescriptor* method) const = 0;
virtual const Message& GetResponsePrototype(
const MethodDescriptor* method) const = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Service);
};
// An RpcController mediates a single method call. The primary purpose of
// the controller is to provide a way to manipulate settings specific to the
// RPC implementation and to find out about RPC-level errors.
//
// The methods provided by the RpcController interface are intended to be a
// "least common denominator" set of features which we expect all
// implementations to support. Specific implementations may provide more
// advanced features (e.g. deadline propagation).
class LIBPROTOBUF_EXPORT RpcController {
public:
inline RpcController() {}
virtual ~RpcController();
// Client-side methods ---------------------------------------------
// These calls may be made from the client side only. Their results
// are undefined on the server side (may crash).
// Resets the RpcController to its initial state so that it may be reused in
// a new call. Must not be called while an RPC is in progress.
virtual void Reset() = 0;
// After a call has finished, returns true if the call failed. The possible
// reasons for failure depend on the RPC implementation. Failed() must not
// be called before a call has finished. If Failed() returns true, the
// contents of the response message are undefined.
virtual bool Failed() const = 0;
// If Failed() is true, returns a human-readable description of the error.
virtual string ErrorText() const = 0;
// Advises the RPC system that the caller desires that the RPC call be
// canceled. The RPC system may cancel it immediately, may wait awhile and
// then cancel it, or may not even cancel the call at all. If the call is
// canceled, the "done" callback will still be called and the RpcController
// will indicate that the call failed at that time.
virtual void StartCancel() = 0;
// Server-side methods ---------------------------------------------
// These calls may be made from the server side only. Their results
// are undefined on the client side (may crash).
// Causes Failed() to return true on the client side. "reason" will be
// incorporated into the message returned by ErrorText(). If you find
// you need to return machine-readable information about failures, you
// should incorporate it into your response protocol buffer and should
// NOT call SetFailed().
virtual void SetFailed(const string& reason) = 0;
// If true, indicates that the client canceled the RPC, so the server may
// as well give up on replying to it. The server should still call the
// final "done" callback.
virtual bool IsCanceled() const = 0;
// Asks that the given callback be called when the RPC is canceled. The
// callback will always be called exactly once. If the RPC completes without
// being canceled, the callback will be called after completion. If the RPC
// has already been canceled when NotifyOnCancel() is called, the callback
// will be called immediately.
//
// NotifyOnCancel() must be called no more than once per request.
virtual void NotifyOnCancel(Closure* callback) = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcController);
};
// Abstract interface for an RPC channel. An RpcChannel represents a
// communication line to a Service which can be used to call that Service's
// methods. The Service may be running on another machine. Normally, you
// should not call an RpcChannel directly, but instead construct a stub Service
// wrapping it. Example:
// RpcChannel* channel = new MyRpcChannel("remotehost.example.com:1234");
// MyService* service = new MyService::Stub(channel);
// service->MyMethod(request, &response, callback);
class LIBPROTOBUF_EXPORT RpcChannel {
public:
inline RpcChannel() {}
virtual ~RpcChannel();
// Call the given method of the remote service. The signature of this
// procedure looks the same as Service::CallMethod(), but the requirements
// are less strict in one important way: the request and response objects
// need not be of any specific class as long as their descriptors are
// method->input_type() and method->output_type().
virtual void CallMethod(const MethodDescriptor* method,
RpcController* controller,
const Message* request,
Message* response,
Closure* done) = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcChannel);
};
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_SERVICE_H__

View file

@ -0,0 +1,365 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/once.h>
#include <stdio.h>
#include <errno.h>
#include <vector>
#include "config.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN // We only need minimal includes
#include <windows.h>
#define snprintf _snprintf // see comment in strutil.cc
#elif defined(HAVE_PTHREAD)
#include <pthread.h>
#else
#error "No suitable threading library available."
#endif
namespace google {
namespace protobuf {
namespace internal {
void VerifyVersion(int headerVersion,
int minLibraryVersion,
const char* filename) {
if (GOOGLE_PROTOBUF_VERSION < minLibraryVersion) {
// Library is too old for headers.
GOOGLE_LOG(FATAL)
<< "This program requires version " << VersionString(minLibraryVersion)
<< " of the Protocol Buffer runtime library, but the installed version "
"is " << VersionString(GOOGLE_PROTOBUF_VERSION) << ". Please update "
"your library. If you compiled the program yourself, make sure that "
"your headers are from the same version of Protocol Buffers as your "
"link-time library. (Version verification failed in \""
<< filename << "\".)";
}
if (headerVersion < kMinHeaderVersionForLibrary) {
// Headers are too old for library.
GOOGLE_LOG(FATAL)
<< "This program was compiled against version "
<< VersionString(headerVersion) << " of the Protocol Buffer runtime "
"library, which is not compatible with the installed version ("
<< VersionString(GOOGLE_PROTOBUF_VERSION) << "). Contact the program "
"author for an update. If you compiled the program yourself, make "
"sure that your headers are from the same version of Protocol Buffers "
"as your link-time library. (Version verification failed in \""
<< filename << "\".)";
}
}
string VersionString(int version) {
int major = version / 1000000;
int minor = (version / 1000) % 1000;
int micro = version % 1000;
// 128 bytes should always be enough, but we use snprintf() anyway to be
// safe.
char buffer[128];
snprintf(buffer, sizeof(buffer), "%d.%d.%d", major, minor, micro);
// Guard against broken MSVC snprintf().
buffer[sizeof(buffer)-1] = '\0';
return buffer;
}
} // namespace internal
// ===================================================================
// emulates google3/base/logging.cc
namespace internal {
void DefaultLogHandler(LogLevel level, const char* filename, int line,
const string& message) {
static const char* level_names[] = { "INFO", "WARNING", "ERROR", "FATAL" };
// We use fprintf() instead of cerr because we want this to work at static
// initialization time.
fprintf(stderr, "libprotobuf %s %s:%d] %s\n",
level_names[level], filename, line, message.c_str());
fflush(stderr); // Needed on MSVC.
}
void NullLogHandler(LogLevel level, const char* filename, int line,
const string& message) {
// Nothing.
}
static LogHandler* log_handler_ = &DefaultLogHandler;
static int log_silencer_count_ = 0;
static Mutex* log_silencer_count_mutex_ = NULL;
GOOGLE_PROTOBUF_DECLARE_ONCE(log_silencer_count_init_);
void DeleteLogSilencerCount() {
delete log_silencer_count_mutex_;
log_silencer_count_mutex_ = NULL;
}
void InitLogSilencerCount() {
log_silencer_count_mutex_ = new Mutex;
OnShutdown(&DeleteLogSilencerCount);
}
void InitLogSilencerCountOnce() {
GoogleOnceInit(&log_silencer_count_init_, &InitLogSilencerCount);
}
LogMessage& LogMessage::operator<<(const string& value) {
message_ += value;
return *this;
}
LogMessage& LogMessage::operator<<(const char* value) {
message_ += value;
return *this;
}
// Since this is just for logging, we don't care if the current locale changes
// the results -- in fact, we probably prefer that. So we use snprintf()
// instead of Simple*toa().
#undef DECLARE_STREAM_OPERATOR
#define DECLARE_STREAM_OPERATOR(TYPE, FORMAT) \
LogMessage& LogMessage::operator<<(TYPE value) { \
/* 128 bytes should be big enough for any of the primitive */ \
/* values which we print with this, but well use snprintf() */ \
/* anyway to be extra safe. */ \
char buffer[128]; \
snprintf(buffer, sizeof(buffer), FORMAT, value); \
/* Guard against broken MSVC snprintf(). */ \
buffer[sizeof(buffer)-1] = '\0'; \
message_ += buffer; \
return *this; \
}
DECLARE_STREAM_OPERATOR(char , "%c" )
DECLARE_STREAM_OPERATOR(int , "%d" )
DECLARE_STREAM_OPERATOR(uint , "%u" )
DECLARE_STREAM_OPERATOR(long , "%ld")
DECLARE_STREAM_OPERATOR(unsigned long, "%lu")
DECLARE_STREAM_OPERATOR(double , "%g" )
#undef DECLARE_STREAM_OPERATOR
LogMessage::LogMessage(LogLevel level, const char* filename, int line)
: level_(level), filename_(filename), line_(line) {}
LogMessage::~LogMessage() {}
void LogMessage::Finish() {
bool suppress = false;
if (level_ != LOGLEVEL_FATAL) {
InitLogSilencerCountOnce();
MutexLock lock(log_silencer_count_mutex_);
suppress = internal::log_silencer_count_ > 0;
}
if (!suppress) {
internal::log_handler_(level_, filename_, line_, message_);
}
if (level_ == LOGLEVEL_FATAL) {
abort();
}
}
void LogFinisher::operator=(LogMessage& other) {
other.Finish();
}
} // namespace internal
LogHandler* SetLogHandler(LogHandler* new_func) {
LogHandler* old = internal::log_handler_;
if (old == &internal::NullLogHandler) {
old = NULL;
}
if (new_func == NULL) {
internal::log_handler_ = &internal::NullLogHandler;
} else {
internal::log_handler_ = new_func;
}
return old;
}
LogSilencer::LogSilencer() {
internal::InitLogSilencerCountOnce();
MutexLock lock(internal::log_silencer_count_mutex_);
++internal::log_silencer_count_;
};
LogSilencer::~LogSilencer() {
internal::InitLogSilencerCountOnce();
MutexLock lock(internal::log_silencer_count_mutex_);
--internal::log_silencer_count_;
};
// ===================================================================
// emulates google3/base/callback.cc
Closure::~Closure() {}
namespace internal { FunctionClosure0::~FunctionClosure0() {} }
void DoNothing() {}
// ===================================================================
// emulates google3/base/mutex.cc
#ifdef _WIN32
struct Mutex::Internal {
CRITICAL_SECTION mutex;
#ifndef NDEBUG
// Used only to implement AssertHeld().
DWORD thread_id;
#endif
};
Mutex::Mutex()
: mInternal(new Internal) {
InitializeCriticalSection(&mInternal->mutex);
}
Mutex::~Mutex() {
DeleteCriticalSection(&mInternal->mutex);
delete mInternal;
}
void Mutex::Lock() {
EnterCriticalSection(&mInternal->mutex);
#ifndef NDEBUG
mInternal->thread_id = GetCurrentThreadId();
#endif
}
void Mutex::Unlock() {
#ifndef NDEBUG
mInternal->thread_id = 0;
#endif
LeaveCriticalSection(&mInternal->mutex);
}
void Mutex::AssertHeld() {
#ifndef NDEBUG
GOOGLE_DCHECK_EQ(mInternal->thread_id, GetCurrentThreadId());
#endif
}
#elif defined(HAVE_PTHREAD)
struct Mutex::Internal {
pthread_mutex_t mutex;
};
Mutex::Mutex()
: mInternal(new Internal) {
pthread_mutex_init(&mInternal->mutex, NULL);
}
Mutex::~Mutex() {
pthread_mutex_destroy(&mInternal->mutex);
delete mInternal;
}
void Mutex::Lock() {
int result = pthread_mutex_lock(&mInternal->mutex);
if (result != 0) {
GOOGLE_LOG(FATAL) << "pthread_mutex_lock: " << strerror(result);
}
}
void Mutex::Unlock() {
int result = pthread_mutex_unlock(&mInternal->mutex);
if (result != 0) {
GOOGLE_LOG(FATAL) << "pthread_mutex_unlock: " << strerror(result);
}
}
void Mutex::AssertHeld() {
// pthreads dosn't provide a way to check which thread holds the mutex.
// TODO(kenton): Maybe keep track of locking thread ID like with WIN32?
}
#endif
// ===================================================================
// Shutdown support.
namespace internal {
typedef void OnShutdownFunc();
vector<void (*)()>* shutdown_functions = NULL;
Mutex* shutdown_functions_mutex = NULL;
GOOGLE_PROTOBUF_DECLARE_ONCE(shutdown_functions_init);
void InitShutdownFunctions() {
shutdown_functions = new vector<void (*)()>;
shutdown_functions_mutex = new Mutex;
}
inline void InitShutdownFunctionsOnce() {
GoogleOnceInit(&shutdown_functions_init, &InitShutdownFunctions);
}
void OnShutdown(void (*func)()) {
InitShutdownFunctionsOnce();
MutexLock lock(shutdown_functions_mutex);
shutdown_functions->push_back(func);
}
} // namespace internal
void ShutdownProtobufLibrary() {
internal::InitShutdownFunctionsOnce();
// We don't need to lock shutdown_functions_mutex because it's up to the
// caller to make sure that no one is using the library before this is
// called.
// Make it safe to call this multiple times.
if (internal::shutdown_functions == NULL) return;
for (int i = 0; i < internal::shutdown_functions->size(); i++) {
internal::shutdown_functions->at(i)();
}
delete internal::shutdown_functions;
internal::shutdown_functions = NULL;
delete internal::shutdown_functions_mutex;
internal::shutdown_functions_mutex = NULL;
}
} // namespace protobuf
} // namespace google

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,345 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
#include <vector>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include "config.h"
namespace google {
namespace protobuf {
namespace {
// TODO(kenton): More tests.
#ifdef PACKAGE_VERSION // only defined when using automake, not MSVC
TEST(VersionTest, VersionMatchesConfig) {
// Verify that the version string specified in config.h matches the one
// in common.h. The config.h version is a string which may have a suffix
// like "beta" or "rc1", so we remove that.
string version = PACKAGE_VERSION;
int pos = 0;
while (pos < version.size() &&
(ascii_isdigit(version[pos]) || version[pos] == '.')) {
++pos;
}
version.erase(pos);
EXPECT_EQ(version, internal::VersionString(GOOGLE_PROTOBUF_VERSION));
}
#endif // PACKAGE_VERSION
TEST(CommonTest, IntMinMaxConstants) {
// kint32min was declared incorrectly in the first release of protobufs.
// Ugh.
EXPECT_LT(kint32min, kint32max);
EXPECT_EQ(static_cast<uint32>(kint32min), static_cast<uint32>(kint32max) + 1);
EXPECT_LT(kint64min, kint64max);
EXPECT_EQ(static_cast<uint64>(kint64min), static_cast<uint64>(kint64max) + 1);
EXPECT_EQ(0, kuint32max + 1);
EXPECT_EQ(0, kuint64max + 1);
}
vector<string> captured_messages_;
void CaptureLog(LogLevel level, const char* filename, int line,
const string& message) {
captured_messages_.push_back(
strings::Substitute("$0 $1:$2: $3",
implicit_cast<int>(level), filename, line, message));
}
TEST(LoggingTest, DefaultLogging) {
CaptureTestStderr();
int line = __LINE__;
GOOGLE_LOG(INFO ) << "A message.";
GOOGLE_LOG(WARNING) << "A warning.";
GOOGLE_LOG(ERROR ) << "An error.";
string text = GetCapturedTestStderr();
EXPECT_EQ(
"libprotobuf INFO "__FILE__":" + SimpleItoa(line + 1) + "] A message.\n"
"libprotobuf WARNING "__FILE__":" + SimpleItoa(line + 2) + "] A warning.\n"
"libprotobuf ERROR "__FILE__":" + SimpleItoa(line + 3) + "] An error.\n",
text);
}
TEST(LoggingTest, NullLogging) {
LogHandler* old_handler = SetLogHandler(NULL);
CaptureTestStderr();
GOOGLE_LOG(INFO ) << "A message.";
GOOGLE_LOG(WARNING) << "A warning.";
GOOGLE_LOG(ERROR ) << "An error.";
EXPECT_TRUE(SetLogHandler(old_handler) == NULL);
string text = GetCapturedTestStderr();
EXPECT_EQ("", text);
}
TEST(LoggingTest, CaptureLogging) {
captured_messages_.clear();
LogHandler* old_handler = SetLogHandler(&CaptureLog);
int start_line = __LINE__;
GOOGLE_LOG(ERROR) << "An error.";
GOOGLE_LOG(WARNING) << "A warning.";
EXPECT_TRUE(SetLogHandler(old_handler) == &CaptureLog);
ASSERT_EQ(2, captured_messages_.size());
EXPECT_EQ(
"2 "__FILE__":" + SimpleItoa(start_line + 1) + ": An error.",
captured_messages_[0]);
EXPECT_EQ(
"1 "__FILE__":" + SimpleItoa(start_line + 2) + ": A warning.",
captured_messages_[1]);
}
TEST(LoggingTest, SilenceLogging) {
captured_messages_.clear();
LogHandler* old_handler = SetLogHandler(&CaptureLog);
int line1 = __LINE__; GOOGLE_LOG(INFO) << "Visible1";
LogSilencer* silencer1 = new LogSilencer;
GOOGLE_LOG(INFO) << "Not visible.";
LogSilencer* silencer2 = new LogSilencer;
GOOGLE_LOG(INFO) << "Not visible.";
delete silencer1;
GOOGLE_LOG(INFO) << "Not visible.";
delete silencer2;
int line2 = __LINE__; GOOGLE_LOG(INFO) << "Visible2";
EXPECT_TRUE(SetLogHandler(old_handler) == &CaptureLog);
ASSERT_EQ(2, captured_messages_.size());
EXPECT_EQ(
"0 "__FILE__":" + SimpleItoa(line1) + ": Visible1",
captured_messages_[0]);
EXPECT_EQ(
"0 "__FILE__":" + SimpleItoa(line2) + ": Visible2",
captured_messages_[1]);
}
class ClosureTest : public testing::Test {
public:
void SetA123Method() { a_ = 123; }
static void SetA123Function() { current_instance_->a_ = 123; }
void SetAMethod(int a) { a_ = a; }
void SetCMethod(string c) { c_ = c; }
static void SetAFunction(int a) { current_instance_->a_ = a; }
static void SetCFunction(string c) { current_instance_->c_ = c; }
void SetABMethod(int a, const char* b) { a_ = a; b_ = b; }
static void SetABFunction(int a, const char* b) {
current_instance_->a_ = a;
current_instance_->b_ = b;
}
virtual void SetUp() {
current_instance_ = this;
a_ = 0;
b_ = NULL;
c_.clear();
}
int a_;
const char* b_;
string c_;
static ClosureTest* current_instance_;
};
ClosureTest* ClosureTest::current_instance_ = NULL;
TEST_F(ClosureTest, TestClosureFunction0) {
Closure* closure = NewCallback(&SetA123Function);
EXPECT_NE(123, a_);
closure->Run();
EXPECT_EQ(123, a_);
}
TEST_F(ClosureTest, TestClosureMethod0) {
Closure* closure = NewCallback(current_instance_,
&ClosureTest::SetA123Method);
EXPECT_NE(123, a_);
closure->Run();
EXPECT_EQ(123, a_);
}
TEST_F(ClosureTest, TestClosureFunction1) {
Closure* closure = NewCallback(&SetAFunction, 456);
EXPECT_NE(456, a_);
closure->Run();
EXPECT_EQ(456, a_);
}
TEST_F(ClosureTest, TestClosureMethod1) {
Closure* closure = NewCallback(current_instance_,
&ClosureTest::SetAMethod, 456);
EXPECT_NE(456, a_);
closure->Run();
EXPECT_EQ(456, a_);
}
TEST_F(ClosureTest, TestClosureFunction1String) {
Closure* closure = NewCallback(&SetCFunction, string("test"));
EXPECT_NE("test", c_);
closure->Run();
EXPECT_EQ("test", c_);
}
TEST_F(ClosureTest, TestClosureMethod1String) {
Closure* closure = NewCallback(current_instance_,
&ClosureTest::SetCMethod, string("test"));
EXPECT_NE("test", c_);
closure->Run();
EXPECT_EQ("test", c_);
}
TEST_F(ClosureTest, TestClosureFunction2) {
const char* cstr = "hello";
Closure* closure = NewCallback(&SetABFunction, 789, cstr);
EXPECT_NE(789, a_);
EXPECT_NE(cstr, b_);
closure->Run();
EXPECT_EQ(789, a_);
EXPECT_EQ(cstr, b_);
}
TEST_F(ClosureTest, TestClosureMethod2) {
const char* cstr = "hello";
Closure* closure = NewCallback(current_instance_,
&ClosureTest::SetABMethod, 789, cstr);
EXPECT_NE(789, a_);
EXPECT_NE(cstr, b_);
closure->Run();
EXPECT_EQ(789, a_);
EXPECT_EQ(cstr, b_);
}
// Repeat all of the above with NewPermanentCallback()
TEST_F(ClosureTest, TestPermanentClosureFunction0) {
Closure* closure = NewPermanentCallback(&SetA123Function);
EXPECT_NE(123, a_);
closure->Run();
EXPECT_EQ(123, a_);
a_ = 0;
closure->Run();
EXPECT_EQ(123, a_);
delete closure;
}
TEST_F(ClosureTest, TestPermanentClosureMethod0) {
Closure* closure = NewPermanentCallback(current_instance_,
&ClosureTest::SetA123Method);
EXPECT_NE(123, a_);
closure->Run();
EXPECT_EQ(123, a_);
a_ = 0;
closure->Run();
EXPECT_EQ(123, a_);
delete closure;
}
TEST_F(ClosureTest, TestPermanentClosureFunction1) {
Closure* closure = NewPermanentCallback(&SetAFunction, 456);
EXPECT_NE(456, a_);
closure->Run();
EXPECT_EQ(456, a_);
a_ = 0;
closure->Run();
EXPECT_EQ(456, a_);
delete closure;
}
TEST_F(ClosureTest, TestPermanentClosureMethod1) {
Closure* closure = NewPermanentCallback(current_instance_,
&ClosureTest::SetAMethod, 456);
EXPECT_NE(456, a_);
closure->Run();
EXPECT_EQ(456, a_);
a_ = 0;
closure->Run();
EXPECT_EQ(456, a_);
delete closure;
}
TEST_F(ClosureTest, TestPermanentClosureFunction2) {
const char* cstr = "hello";
Closure* closure = NewPermanentCallback(&SetABFunction, 789, cstr);
EXPECT_NE(789, a_);
EXPECT_NE(cstr, b_);
closure->Run();
EXPECT_EQ(789, a_);
EXPECT_EQ(cstr, b_);
a_ = 0;
b_ = NULL;
closure->Run();
EXPECT_EQ(789, a_);
EXPECT_EQ(cstr, b_);
delete closure;
}
TEST_F(ClosureTest, TestPermanentClosureMethod2) {
const char* cstr = "hello";
Closure* closure = NewPermanentCallback(current_instance_,
&ClosureTest::SetABMethod, 789, cstr);
EXPECT_NE(789, a_);
EXPECT_NE(cstr, b_);
closure->Run();
EXPECT_EQ(789, a_);
EXPECT_EQ(cstr, b_);
a_ = 0;
b_ = NULL;
closure->Run();
EXPECT_EQ(789, a_);
EXPECT_EQ(cstr, b_);
delete closure;
}
} // anonymous namespace
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,41 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
#include <google/protobuf/stubs/hash.h>
namespace google {
namespace protobuf {
// Nothing needed here right now.
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,220 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
//
// Deals with the fact that hash_map is not defined everywhere.
#ifndef GOOGLE_PROTOBUF_STUBS_HASH_H__
#define GOOGLE_PROTOBUF_STUBS_HASH_H__
#include <string.h>
#include <google/protobuf/stubs/common.h>
#include "config.h"
#if defined(HAVE_HASH_MAP) && defined(HAVE_HASH_SET)
#include HASH_MAP_H
#include HASH_SET_H
#else
#define MISSING_HASH
#include <map>
#include <set>
#endif
namespace google {
namespace protobuf {
#ifdef MISSING_HASH
// This system doesn't have hash_map or hash_set. Emulate them using map and
// set.
// Make hash<T> be the same as less<T>. Note that everywhere where custom
// hash functions are defined in the protobuf code, they are also defined such
// that they can be used as "less" functions, which is required by MSVC anyway.
template <typename Key>
struct hash {
// Dummy, just to make derivative hash functions compile.
int operator()(const Key& key) {
GOOGLE_LOG(FATAL) << "Should never be called.";
return 0;
}
inline bool operator()(const Key& a, const Key& b) const {
return a < b;
}
};
// Make sure char* is compared by value.
template <>
struct hash<const char*> {
// Dummy, just to make derivative hash functions compile.
int operator()(const char* key) {
GOOGLE_LOG(FATAL) << "Should never be called.";
return 0;
}
inline bool operator()(const char* a, const char* b) const {
return strcmp(a, b) < 0;
}
};
template <typename Key, typename Data,
typename HashFcn = hash<Key>,
typename EqualKey = int >
class hash_map : public std::map<Key, Data, HashFcn> {
};
template <typename Key,
typename HashFcn = hash<Key>,
typename EqualKey = int >
class hash_set : public std::set<Key, HashFcn> {
};
#elif defined(_MSC_VER) && !defined(_STLPORT_VERSION)
template <typename Key>
struct hash : public HASH_NAMESPACE::hash_compare<Key> {
};
// MSVC's hash_compare<const char*> hashes based on the string contents but
// compares based on the string pointer. WTF?
class CstringLess {
public:
inline bool operator()(const char* a, const char* b) const {
return strcmp(a, b) < 0;
}
};
template <>
struct hash<const char*>
: public HASH_NAMESPACE::hash_compare<const char*, CstringLess> {
};
template <typename Key, typename Data,
typename HashFcn = hash<Key>,
typename EqualKey = int >
class hash_map : public HASH_NAMESPACE::hash_map<
Key, Data, HashFcn> {
};
template <typename Key,
typename HashFcn = hash<Key>,
typename EqualKey = int >
class hash_set : public HASH_NAMESPACE::hash_set<
Key, HashFcn> {
};
#else
template <typename Key>
struct hash : public HASH_NAMESPACE::hash<Key> {
};
template <typename Key>
struct hash<const Key*> {
inline size_t operator()(const Key* key) const {
return reinterpret_cast<size_t>(key);
}
};
// Unlike the old SGI version, the TR1 "hash" does not special-case char*. So,
// we go ahead and provide our own implementation.
template <>
struct hash<const char*> {
inline size_t operator()(const char* str) const {
size_t result = 0;
for (; *str != '\0'; str++) {
result = 5 * result + *str;
}
return result;
}
};
template <typename Key, typename Data,
typename HashFcn = hash<Key>,
typename EqualKey = std::equal_to<Key> >
class hash_map : public HASH_NAMESPACE::HASH_MAP_CLASS<
Key, Data, HashFcn, EqualKey> {
};
template <typename Key,
typename HashFcn = hash<Key>,
typename EqualKey = std::equal_to<Key> >
class hash_set : public HASH_NAMESPACE::HASH_SET_CLASS<
Key, HashFcn, EqualKey> {
};
#endif
template <>
struct hash<string> {
inline size_t operator()(const string& key) const {
return hash<const char*>()(key.c_str());
}
static const size_t bucket_size = 4;
static const size_t min_buckets = 8;
inline size_t operator()(const string& a, const string& b) const {
return a < b;
}
};
template <typename First, typename Second>
struct hash<pair<First, Second> > {
inline size_t operator()(const pair<First, Second>& key) const {
size_t first_hash = hash<First>()(key.first);
size_t second_hash = hash<Second>()(key.second);
// FIXME(kenton): What is the best way to compute this hash? I have
// no idea! This seems a bit better than an XOR.
return first_hash * ((1 << 16) - 1) + second_hash;
}
static const size_t bucket_size = 4;
static const size_t min_buckets = 8;
inline size_t operator()(const pair<First, Second>& a,
const pair<First, Second>& b) const {
return a < b;
}
};
// Used by GCC/SGI STL only. (Why isn't this provided by the standard
// library? :( )
struct streq {
inline bool operator()(const char* a, const char* b) const {
return strcmp(a, b) == 0;
}
};
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_STUBS_HASH_H__

View file

@ -0,0 +1,119 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// from google3/util/gtl/map-util.h
// Author: Anton Carver
#ifndef GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__
#define GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
// Perform a lookup in a map or hash_map.
// If the key is present in the map then the value associated with that
// key is returned, otherwise the value passed as a default is returned.
template <class Collection>
const typename Collection::value_type::second_type&
FindWithDefault(const Collection& collection,
const typename Collection::value_type::first_type& key,
const typename Collection::value_type::second_type& value) {
typename Collection::const_iterator it = collection.find(key);
if (it == collection.end()) {
return value;
}
return it->second;
}
// Perform a lookup in a map or hash_map.
// If the key is present a const pointer to the associated value is returned,
// otherwise a NULL pointer is returned.
template <class Collection>
const typename Collection::value_type::second_type*
FindOrNull(const Collection& collection,
const typename Collection::value_type::first_type& key) {
typename Collection::const_iterator it = collection.find(key);
if (it == collection.end()) {
return 0;
}
return &it->second;
}
// Perform a lookup in a map or hash_map whose values are pointers.
// If the key is present a const pointer to the associated value is returned,
// otherwise a NULL pointer is returned.
// This function does not distinguish between a missing key and a key mapped
// to a NULL value.
template <class Collection>
const typename Collection::value_type::second_type
FindPtrOrNull(const Collection& collection,
const typename Collection::value_type::first_type& key) {
typename Collection::const_iterator it = collection.find(key);
if (it == collection.end()) {
return 0;
}
return it->second;
}
// Change the value associated with a particular key in a map or hash_map.
// If the key is not present in the map the key and value are inserted,
// otherwise the value is updated to be a copy of the value provided.
// True indicates that an insert took place, false indicates an update.
template <class Collection, class Key, class Value>
bool InsertOrUpdate(Collection * const collection,
const Key& key, const Value& value) {
pair<typename Collection::iterator, bool> ret =
collection->insert(typename Collection::value_type(key, value));
if (!ret.second) {
// update
ret.first->second = value;
return false;
}
return true;
}
// Insert a new key and value into a map or hash_map.
// If the key is not present in the map the key and value are
// inserted, otherwise nothing happens. True indicates that an insert
// took place, false indicates the key was already present.
template <class Collection, class Key, class Value>
bool InsertIfNotPresent(Collection * const collection,
const Key& key, const Value& value) {
pair<typename Collection::iterator, bool> ret =
collection->insert(typename Collection::value_type(key, value));
return ret.second;
}
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__

View file

@ -0,0 +1,88 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
//
// emulates google3/base/once.h
//
// This header is intended to be included only by internal .cc files and
// generated .pb.cc files. Users should not use this directly.
#ifdef _WIN32
#include <windows.h>
#endif
#include <google/protobuf/stubs/once.h>
namespace google {
namespace protobuf {
#ifdef _WIN32
struct ProtobufOnceInternal {
ProtobufOnceInternal() {
InitializeCriticalSection(&critical_section);
}
~ProtobufOnceInternal() {
DeleteCriticalSection(&critical_section);
}
CRITICAL_SECTION critical_section;
};
ProtobufOnceType::~ProtobufOnceType()
{
delete internal_;
internal_ = NULL;
}
ProtobufOnceType::ProtobufOnceType() {
// internal_ may be non-NULL if Init() was already called.
if (internal_ == NULL) internal_ = new ProtobufOnceInternal;
}
void ProtobufOnceType::Init(void (*init_func)()) {
// internal_ may be NULL if we're still in dynamic initialization and the
// constructor has not been called yet. As mentioned in once.h, we assume
// that the program is still single-threaded at this time, and therefore it
// should be safe to initialize internal_ like so.
if (internal_ == NULL) internal_ = new ProtobufOnceInternal;
EnterCriticalSection(&internal_->critical_section);
if (!initialized_) {
init_func();
initialized_ = true;
}
LeaveCriticalSection(&internal_->critical_section);
}
#endif
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,123 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
//
// emulates google3/base/once.h
//
// This header is intended to be included only by internal .cc files and
// generated .pb.cc files. Users should not use this directly.
//
// This is basically a portable version of pthread_once().
//
// This header declares three things:
// * A type called ProtobufOnceType.
// * A macro GOOGLE_PROTOBUF_DECLARE_ONCE() which declares a variable of type
// ProtobufOnceType. This is the only legal way to declare such a variable.
// The macro may only be used at the global scope (you cannot create local
// or class member variables of this type).
// * A function GogoleOnceInit(ProtobufOnceType* once, void (*init_func)()).
// This function, when invoked multiple times given the same ProtobufOnceType
// object, will invoke init_func on the first call only, and will make sure
// none of the calls return before that first call to init_func has finished.
//
// This implements a way to perform lazy initialization. It's more efficient
// than using mutexes as no lock is needed if initialization has already
// happened.
//
// Example usage:
// void Init();
// GOOGLE_PROTOBUF_DECLARE_ONCE(once_init);
//
// // Calls Init() exactly once.
// void InitOnce() {
// GoogleOnceInit(&once_init, &Init);
// }
//
// Note that if GoogleOnceInit() is called before main() has begun, it must
// only be called by the thread that will eventually call main() -- that is,
// the thread that performs dynamic initialization. In general this is a safe
// assumption since people don't usually construct threads before main() starts,
// but it is technically not guaranteed. Unfortunately, Win32 provides no way
// whatsoever to statically-initialize its synchronization primitives, so our
// only choice is to assume that dynamic initialization is single-threaded.
#ifndef GOOGLE_PROTOBUF_STUBS_ONCE_H__
#define GOOGLE_PROTOBUF_STUBS_ONCE_H__
#include <google/protobuf/stubs/common.h>
#ifndef _WIN32
#include <pthread.h>
#endif
namespace google {
namespace protobuf {
#ifdef _WIN32
struct ProtobufOnceInternal;
struct LIBPROTOBUF_EXPORT ProtobufOnceType {
ProtobufOnceType();
~ProtobufOnceType();
void Init(void (*init_func)());
volatile bool initialized_;
ProtobufOnceInternal* internal_;
};
#define GOOGLE_PROTOBUF_DECLARE_ONCE(NAME) \
::google::protobuf::ProtobufOnceType NAME
inline void GoogleOnceInit(ProtobufOnceType* once, void (*init_func)()) {
// Note: Double-checked locking is safe on x86.
if (!once->initialized_) {
once->Init(init_func);
}
}
#else
typedef pthread_once_t ProtobufOnceType;
#define GOOGLE_PROTOBUF_DECLARE_ONCE(NAME) \
pthread_once_t NAME = PTHREAD_ONCE_INIT
inline void GoogleOnceInit(ProtobufOnceType* once, void (*init_func)()) {
pthread_once(once, init_func);
}
#endif
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_STUBS_ONCE_H__

View file

@ -0,0 +1,253 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <pthread.h>
#endif
#include <google/protobuf/stubs/once.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace {
class OnceInitTest : public testing::Test {
protected:
void SetUp() {
state_ = INIT_NOT_STARTED;
current_test_ = this;
}
// Since ProtobufOnceType is only allowed to be allocated in static storage,
// each test must use a different pair of ProtobufOnceType objects which it
// must declare itself.
void SetOnces(ProtobufOnceType* once, ProtobufOnceType* recursive_once) {
once_ = once;
recursive_once_ = recursive_once;
}
void InitOnce() {
GoogleOnceInit(once_, &InitStatic);
}
void InitRecursiveOnce() {
GoogleOnceInit(recursive_once_, &InitRecursiveStatic);
}
void BlockInit() { init_blocker_.Lock(); }
void UnblockInit() { init_blocker_.Unlock(); }
class TestThread {
public:
TestThread(Closure* callback)
: done_(false), joined_(false), callback_(callback) {
#ifdef _WIN32
thread_ = CreateThread(NULL, 0, &Start, this, 0, NULL);
#else
pthread_create(&thread_, NULL, &Start, this);
#endif
}
~TestThread() {
if (!joined_) Join();
}
bool IsDone() {
MutexLock lock(&done_mutex_);
return done_;
}
void Join() {
joined_ = true;
#ifdef _WIN32
WaitForSingleObject(thread_, INFINITE);
CloseHandle(thread_);
#else
pthread_join(thread_, NULL);
#endif
}
private:
#ifdef _WIN32
HANDLE thread_;
#else
pthread_t thread_;
#endif
Mutex done_mutex_;
bool done_;
bool joined_;
Closure* callback_;
#ifdef _WIN32
static DWORD WINAPI Start(LPVOID arg) {
#else
static void* Start(void* arg) {
#endif
reinterpret_cast<TestThread*>(arg)->Run();
return 0;
}
void Run() {
callback_->Run();
MutexLock lock(&done_mutex_);
done_ = true;
}
};
TestThread* RunInitOnceInNewThread() {
return new TestThread(NewCallback(this, &OnceInitTest::InitOnce));
}
TestThread* RunInitRecursiveOnceInNewThread() {
return new TestThread(NewCallback(this, &OnceInitTest::InitRecursiveOnce));
}
enum State {
INIT_NOT_STARTED,
INIT_STARTED,
INIT_DONE
};
State CurrentState() {
MutexLock lock(&mutex_);
return state_;
}
void WaitABit() {
#ifdef _WIN32
Sleep(1000);
#else
sleep(1);
#endif
}
private:
Mutex mutex_;
Mutex init_blocker_;
State state_;
ProtobufOnceType* once_;
ProtobufOnceType* recursive_once_;
void Init() {
MutexLock lock(&mutex_);
EXPECT_EQ(INIT_NOT_STARTED, state_);
state_ = INIT_STARTED;
mutex_.Unlock();
init_blocker_.Lock();
init_blocker_.Unlock();
mutex_.Lock();
state_ = INIT_DONE;
}
static OnceInitTest* current_test_;
static void InitStatic() { current_test_->Init(); }
static void InitRecursiveStatic() { current_test_->InitOnce(); }
};
OnceInitTest* OnceInitTest::current_test_ = NULL;
GOOGLE_PROTOBUF_DECLARE_ONCE(simple_once);
TEST_F(OnceInitTest, Simple) {
SetOnces(&simple_once, NULL);
EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
InitOnce();
EXPECT_EQ(INIT_DONE, CurrentState());
// Calling again has no effect.
InitOnce();
EXPECT_EQ(INIT_DONE, CurrentState());
}
GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once1);
GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once2);
TEST_F(OnceInitTest, Recursive) {
SetOnces(&recursive_once1, &recursive_once2);
EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
InitRecursiveOnce();
EXPECT_EQ(INIT_DONE, CurrentState());
}
GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_once);
TEST_F(OnceInitTest, MultipleThreads) {
SetOnces(&multiple_threads_once, NULL);
scoped_ptr<TestThread> threads[4];
EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
for (int i = 0; i < 4; i++) {
threads[i].reset(RunInitOnceInNewThread());
}
for (int i = 0; i < 4; i++) {
threads[i]->Join();
}
EXPECT_EQ(INIT_DONE, CurrentState());
}
GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once1);
GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once2);
TEST_F(OnceInitTest, MultipleThreadsBlocked) {
SetOnces(&multiple_threads_blocked_once1, &multiple_threads_blocked_once2);
scoped_ptr<TestThread> threads[8];
EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
BlockInit();
for (int i = 0; i < 4; i++) {
threads[i].reset(RunInitOnceInNewThread());
}
for (int i = 4; i < 8; i++) {
threads[i].reset(RunInitRecursiveOnceInNewThread());
}
WaitABit();
// We should now have one thread blocked inside Init(), four blocked waiting
// for Init() to complete, and three blocked waiting for InitRecursive() to
// complete.
EXPECT_EQ(INIT_STARTED, CurrentState());
UnblockInit();
for (int i = 0; i < 8; i++) {
threads[i]->Join();
}
EXPECT_EQ(INIT_DONE, CurrentState());
}
} // anonymous namespace
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,121 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// from google3/util/gtl/stl_util-inl.h
#ifndef GOOGLE_PROTOBUF_STUBS_STL_UTIL_INL_H__
#define GOOGLE_PROTOBUF_STUBS_STL_UTIL_INL_H__
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
// STLDeleteContainerPointers()
// For a range within a container of pointers, calls delete
// (non-array version) on these pointers.
// NOTE: for these three functions, we could just implement a DeleteObject
// functor and then call for_each() on the range and functor, but this
// requires us to pull in all of algorithm.h, which seems expensive.
// For hash_[multi]set, it is important that this deletes behind the iterator
// because the hash_set may call the hash function on the iterator when it is
// advanced, which could result in the hash function trying to deference a
// stale pointer.
template <class ForwardIterator>
void STLDeleteContainerPointers(ForwardIterator begin,
ForwardIterator end) {
while (begin != end) {
ForwardIterator temp = begin;
++begin;
delete *temp;
}
}
// Inside Google, this function implements a horrible, disgusting hack in which
// we reach into the string's private implementation and resize it without
// initializing the new bytes. In some cases doing this can significantly
// improve performance. However, since it's totally non-portable it has no
// place in open source code. Feel free to fill this function in with your
// own disgusting hack if you want the perf boost.
inline void STLStringResizeUninitialized(string* s, size_t new_size) {
s->resize(new_size);
}
// Return a mutable char* pointing to a string's internal buffer,
// which may not be null-terminated. Writing through this pointer will
// modify the string.
//
// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the
// next call to a string method that invalidates iterators.
//
// As of 2006-04, there is no standard-blessed way of getting a
// mutable reference to a string's internal buffer. However, issue 530
// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530)
// proposes this as the method. According to Matt Austern, this should
// already work on all current implementations.
inline char* string_as_array(string* str) {
// DO NOT USE const_cast<char*>(str->data())! See the unittest for why.
return str->empty() ? NULL : &*str->begin();
}
// STLDeleteElements() deletes all the elements in an STL container and clears
// the container. This function is suitable for use with a vector, set,
// hash_set, or any other STL container which defines sensible begin(), end(),
// and clear() methods.
//
// If container is NULL, this function is a no-op.
//
// As an alternative to calling STLDeleteElements() directly, consider
// ElementDeleter (defined below), which ensures that your container's elements
// are deleted when the ElementDeleter goes out of scope.
template <class T>
void STLDeleteElements(T *container) {
if (!container) return;
STLDeleteContainerPointers(container->begin(), container->end());
container->clear();
}
// Given an STL container consisting of (key, value) pairs, STLDeleteValues
// deletes all the "value" components and clears the container. Does nothing
// in the case it's given a NULL pointer.
template <class T>
void STLDeleteValues(T *v) {
if (!v) return;
for (typename T::iterator i = v->begin(); i != v->end(); ++i) {
delete i->second;
}
v->clear();
}
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_STUBS_STL_UTIL_INL_H__

View file

@ -0,0 +1,536 @@
// Copyright 2005-2008 Google Inc. All Rights Reserved.
// Author: jrm@google.com (Jim Meehan)
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
namespace internal {
// These four-byte entries compactly encode how many bytes 0..255 to delete
// in making a string replacement, how many bytes to add 0..255, and the offset
// 0..64k-1 of the replacement string in remap_string.
struct RemapEntry {
uint8 delete_bytes;
uint8 add_bytes;
uint16 bytes_offset;
};
// Exit type codes for state tables. All but the first get stuffed into
// signed one-byte entries. The first is only generated by executable code.
// To distinguish from next-state entries, these must be contiguous and
// all <= kExitNone
typedef enum {
kExitDstSpaceFull = 239,
kExitIllegalStructure, // 240
kExitOK, // 241
kExitReject, // ...
kExitReplace1,
kExitReplace2,
kExitReplace3,
kExitReplace21,
kExitReplace31,
kExitReplace32,
kExitReplaceOffset1,
kExitReplaceOffset2,
kExitReplace1S0,
kExitSpecial,
kExitDoAgain,
kExitRejectAlt,
kExitNone // 255
} ExitReason;
// This struct represents one entire state table. The three initialized byte
// areas are state_table, remap_base, and remap_string. state0 and state0_size
// give the byte offset and length within state_table of the initial state --
// table lookups are expected to start and end in this state, but for
// truncated UTF-8 strings, may end in a different state. These allow a quick
// test for that condition. entry_shift is 8 for tables subscripted by a full
// byte value and 6 for space-optimized tables subscripted by only six
// significant bits in UTF-8 continuation bytes.
typedef struct {
const uint32 state0;
const uint32 state0_size;
const uint32 total_size;
const int max_expand;
const int entry_shift;
const int bytes_per_entry;
const uint32 losub;
const uint32 hiadd;
const uint8* state_table;
const RemapEntry* remap_base;
const uint8* remap_string;
const uint8* fast_state;
} UTF8StateMachineObj;
typedef UTF8StateMachineObj UTF8ScanObj;
#define X__ (kExitIllegalStructure)
#define RJ_ (kExitReject)
#define S1_ (kExitReplace1)
#define S2_ (kExitReplace2)
#define S3_ (kExitReplace3)
#define S21 (kExitReplace21)
#define S31 (kExitReplace31)
#define S32 (kExitReplace32)
#define T1_ (kExitReplaceOffset1)
#define T2_ (kExitReplaceOffset2)
#define S11 (kExitReplace1S0)
#define SP_ (kExitSpecial)
#define D__ (kExitDoAgain)
#define RJA (kExitRejectAlt)
// Entire table has 9 state blocks of 256 entries each
static const unsigned int utf8acceptnonsurrogates_STATE0 = 0; // state[0]
static const unsigned int utf8acceptnonsurrogates_STATE0_SIZE = 256; // =[1]
static const unsigned int utf8acceptnonsurrogates_TOTAL_SIZE = 2304;
static const unsigned int utf8acceptnonsurrogates_MAX_EXPAND_X4 = 0;
static const unsigned int utf8acceptnonsurrogates_SHIFT = 8;
static const unsigned int utf8acceptnonsurrogates_BYTES = 1;
static const unsigned int utf8acceptnonsurrogates_LOSUB = 0x20202020;
static const unsigned int utf8acceptnonsurrogates_HIADD = 0x00000000;
static const uint8 utf8acceptnonsurrogates[] = {
// state[0] 0x000000 Byte 1
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 3,
4, 5, 5, 5, 6, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
// state[1] 0x000080 Byte 2 of 2
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
// state[2] 0x000000 Byte 2 of 3
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
// state[3] 0x001000 Byte 2 of 3
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
// state[4] 0x000000 Byte 2 of 4
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
// state[5] 0x040000 Byte 2 of 4
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
// state[6] 0x100000 Byte 2 of 4
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
// state[7] 0x00d000 Byte 2 of 3
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
// state[8] 0x00d800 Byte 3 of 3
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,
RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,
RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,
RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_, RJ_,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__, X__,
};
// Remap base[0] = (del, add, string_offset)
static const RemapEntry utf8acceptnonsurrogates_remap_base[] = {
{0, 0, 0} };
// Remap string[0]
static const unsigned char utf8acceptnonsurrogates_remap_string[] = {
0 };
static const unsigned char utf8acceptnonsurrogates_fast[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
static const UTF8ScanObj utf8acceptnonsurrogates_obj = {
utf8acceptnonsurrogates_STATE0,
utf8acceptnonsurrogates_STATE0_SIZE,
utf8acceptnonsurrogates_TOTAL_SIZE,
utf8acceptnonsurrogates_MAX_EXPAND_X4,
utf8acceptnonsurrogates_SHIFT,
utf8acceptnonsurrogates_BYTES,
utf8acceptnonsurrogates_LOSUB,
utf8acceptnonsurrogates_HIADD,
utf8acceptnonsurrogates,
utf8acceptnonsurrogates_remap_base,
utf8acceptnonsurrogates_remap_string,
utf8acceptnonsurrogates_fast
};
#undef X__
#undef RJ_
#undef S1_
#undef S2_
#undef S3_
#undef S21
#undef S31
#undef S32
#undef T1_
#undef T2_
#undef S11
#undef SP_
#undef D__
#undef RJA
// Return true if current Tbl pointer is within state0 range
// Note that unsigned compare checks both ends of range simultaneously
static inline bool InStateZero(const UTF8ScanObj* st, const uint8* Tbl) {
const uint8* Tbl0 = &st->state_table[st->state0];
return (static_cast<uint32>(Tbl - Tbl0) < st->state0_size);
}
// Scan a UTF-8 string based on state table.
// Always scan complete UTF-8 characters
// Set number of bytes scanned. Return reason for exiting
int UTF8GenericScan(const UTF8ScanObj* st,
const char * str,
int str_length,
int* bytes_consumed) {
*bytes_consumed = 0;
if (str_length == 0) return kExitOK;
int eshift = st->entry_shift;
const uint8* isrc = reinterpret_cast<const uint8*>(str);
const uint8* src = isrc;
const uint8* srclimit = isrc + str_length;
const uint8* srclimit8 = srclimit - 7;
const uint8* Tbl_0 = &st->state_table[st->state0];
DoAgain:
// Do state-table scan
int e = 0;
uint8 c;
const uint8* Tbl2 = &st->fast_state[0];
const uint32 losub = st->losub;
const uint32 hiadd = st->hiadd;
// Check initial few bytes one at a time until 8-byte aligned
//----------------------------
while ((((uintptr_t)src & 0x07) != 0) &&
(src < srclimit) &&
Tbl2[src[0]] == 0) {
src++;
}
if (((uintptr_t)src & 0x07) == 0) {
// Do fast for groups of 8 identity bytes.
// This covers a lot of 7-bit ASCII ~8x faster then the 1-byte loop,
// including slowing slightly on cr/lf/ht
//----------------------------
while (src < srclimit8) {
uint32 s0123 = (reinterpret_cast<const uint32 *>(src))[0];
uint32 s4567 = (reinterpret_cast<const uint32 *>(src))[1];
src += 8;
// This is a fast range check for all bytes in [lowsub..0x80-hiadd)
uint32 temp = (s0123 - losub) | (s0123 + hiadd) |
(s4567 - losub) | (s4567 + hiadd);
if ((temp & 0x80808080) != 0) {
// We typically end up here on cr/lf/ht; src was incremented
int e0123 = (Tbl2[src[-8]] | Tbl2[src[-7]]) |
(Tbl2[src[-6]] | Tbl2[src[-5]]);
if (e0123 != 0) {
src -= 8;
break;
} // Exit on Non-interchange
e0123 = (Tbl2[src[-4]] | Tbl2[src[-3]]) |
(Tbl2[src[-2]] | Tbl2[src[-1]]);
if (e0123 != 0) {
src -= 4;
break;
} // Exit on Non-interchange
// Else OK, go around again
}
}
}
//----------------------------
// Byte-at-a-time scan
//----------------------------
const uint8* Tbl = Tbl_0;
while (src < srclimit) {
c = *src;
e = Tbl[c];
src++;
if (e >= kExitIllegalStructure) {break;}
Tbl = &Tbl_0[e << eshift];
}
//----------------------------
// Exit posibilities:
// Some exit code, !state0, back up over last char
// Some exit code, state0, back up one byte exactly
// source consumed, !state0, back up over partial char
// source consumed, state0, exit OK
// For illegal byte in state0, avoid backup up over PREVIOUS char
// For truncated last char, back up to beginning of it
if (e >= kExitIllegalStructure) {
// Back up over exactly one byte of rejected/illegal UTF-8 character
src--;
// Back up more if needed
if (!InStateZero(st, Tbl)) {
do {
src--;
} while ((src > isrc) && ((src[0] & 0xc0) == 0x80));
}
} else if (!InStateZero(st, Tbl)) {
// Back up over truncated UTF-8 character
e = kExitIllegalStructure;
do {
src--;
} while ((src > isrc) && ((src[0] & 0xc0) == 0x80));
} else {
// Normal termination, source fully consumed
e = kExitOK;
}
if (e == kExitDoAgain) {
// Loop back up to the fast scan
goto DoAgain;
}
*bytes_consumed = src - isrc;
return e;
}
int UTF8GenericScanFastAscii(const UTF8ScanObj* st,
const char * str,
int str_length,
int* bytes_consumed) {
*bytes_consumed = 0;
if (str_length == 0) return kExitOK;
const uint8* isrc = reinterpret_cast<const uint8*>(str);
const uint8* src = isrc;
const uint8* srclimit = isrc + str_length;
const uint8* srclimit8 = srclimit - 7;
int n;
int rest_consumed;
int exit_reason;
do {
// Check initial few bytes one at a time until 8-byte aligned
while ((((uintptr_t)src & 0x07) != 0) &&
(src < srclimit) && (src[0] < 0x80)) {
src++;
}
if (((uintptr_t)src & 0x07) == 0) {
while ((src < srclimit8) &&
(((reinterpret_cast<const uint32*>(src)[0] |
reinterpret_cast<const uint32*>(src)[1]) & 0x80808080) == 0)) {
src += 8;
}
}
while ((src < srclimit) && (src[0] < 0x80)) {
src++;
}
// Run state table on the rest
n = src - isrc;
exit_reason = UTF8GenericScan(st, str + n, str_length - n, &rest_consumed);
src += rest_consumed;
} while ( exit_reason == kExitDoAgain );
*bytes_consumed = src - isrc;
return exit_reason;
}
// Hack: On some compilers the static tables are initialized at startup.
// We can't use them until they are initialized. However, some Protocol
// Buffer parsing happens at static init time and may try to validate
// UTF-8 strings. Since UTF-8 validation is only used for debugging
// anyway, we simply always return success if initialization hasn't
// occurred yet.
namespace {
bool module_initialized_ = false;
struct InitDetector {
InitDetector() {
module_initialized_ = true;
}
};
InitDetector init_detector;
} // namespace
bool IsStructurallyValidUTF8(const char* buf, int len) {
if (!module_initialized_) return true;
int bytes_consumed = 0;
UTF8GenericScanFastAscii(&utf8acceptnonsurrogates_obj,
buf, len, &bytes_consumed);
return (bytes_consumed == len);
}
} // namespace internal
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,40 @@
// Copyright 2008 Google Inc. All Rights Reserved.
// Author: xpeng@google.com (Peter Peng)
#include <google/protobuf/stubs/common.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace internal {
namespace {
TEST(StructurallyValidTest, ValidUTF8String) {
// On GCC, this string can be written as:
// "abcd 1234 - \u2014\u2013\u2212"
// MSVC seems to interpret \u differently.
string valid_str("abcd 1234 - \342\200\224\342\200\223\342\210\222 - xyz789");
EXPECT_TRUE(IsStructurallyValidUTF8(valid_str.data(),
valid_str.size()));
// Additional check for pointer alignment
for (int i = 1; i < 8; ++i) {
EXPECT_TRUE(IsStructurallyValidUTF8(valid_str.data() + i,
valid_str.size() - i));
}
}
TEST(StructurallyValidTest, InvalidUTF8String) {
const string invalid_str("abcd\xA0\xB0\xA0\xB0\xA0\xB0 - xyz789");
EXPECT_FALSE(IsStructurallyValidUTF8(invalid_str.data(),
invalid_str.size()));
// Additional check for pointer alignment
for (int i = 1; i < 8; ++i) {
EXPECT_FALSE(IsStructurallyValidUTF8(invalid_str.data() + i,
invalid_str.size() - i));
}
}
} // namespace
} // namespace internal
} // namespace protobuf
} // namespace google

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,459 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// from google3/strings/strutil.h
#ifndef GOOGLE_PROTOBUF_STUBS_STRUTIL_H__
#define GOOGLE_PROTOBUF_STUBS_STRUTIL_H__
#include <stdlib.h>
#include <vector>
#include <google/protobuf/stubs/common.h>
namespace google {
namespace protobuf {
#ifdef _MSC_VER
#define strtoll _strtoi64
#define strtoull _strtoui64
#elif defined(__DECCXX) && defined(__osf__)
// HP C++ on Tru64 does not have strtoll, but strtol is already 64-bit.
#define strtoll strtol
#define strtoull strtoul
#endif
// ----------------------------------------------------------------------
// ascii_isalnum()
// Check if an ASCII character is alphanumeric. We can't use ctype's
// isalnum() because it is affected by locale. This function is applied
// to identifiers in the protocol buffer language, not to natural-language
// strings, so locale should not be taken into account.
// ascii_isdigit()
// Like above, but only accepts digits.
// ----------------------------------------------------------------------
inline bool ascii_isalnum(char c) {
return ('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z') ||
('0' <= c && c <= '9');
}
inline bool ascii_isdigit(char c) {
return ('0' <= c && c <= '9');
}
// ----------------------------------------------------------------------
// HasPrefixString()
// Check if a string begins with a given prefix.
// StripPrefixString()
// Given a string and a putative prefix, returns the string minus the
// prefix string if the prefix matches, otherwise the original
// string.
// ----------------------------------------------------------------------
inline bool HasPrefixString(const string& str,
const string& prefix) {
return str.size() >= prefix.size() &&
str.compare(0, prefix.size(), prefix) == 0;
}
inline string StripPrefixString(const string& str, const string& prefix) {
if (HasPrefixString(str, prefix)) {
return str.substr(prefix.size());
} else {
return str;
}
}
// ----------------------------------------------------------------------
// HasSuffixString()
// Return true if str ends in suffix.
// StripSuffixString()
// Given a string and a putative suffix, returns the string minus the
// suffix string if the suffix matches, otherwise the original
// string.
// ----------------------------------------------------------------------
inline bool HasSuffixString(const string& str,
const string& suffix) {
return str.size() >= suffix.size() &&
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
inline string StripSuffixString(const string& str, const string& suffix) {
if (HasSuffixString(str, suffix)) {
return str.substr(0, str.size() - suffix.size());
} else {
return str;
}
}
// ----------------------------------------------------------------------
// StripString
// Replaces any occurrence of the character 'remove' (or the characters
// in 'remove') with the character 'replacewith'.
// Good for keeping html characters or protocol characters (\t) out
// of places where they might cause a problem.
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT void StripString(string* s, const char* remove,
char replacewith);
// ----------------------------------------------------------------------
// LowerString()
// UpperString()
// Convert the characters in "s" to lowercase or uppercase. ASCII-only:
// these functions intentionally ignore locale because they are applied to
// identifiers used in the Protocol Buffer language, not to natural-language
// strings.
// ----------------------------------------------------------------------
inline void LowerString(string * s) {
string::iterator end = s->end();
for (string::iterator i = s->begin(); i != end; ++i) {
// tolower() changes based on locale. We don't want this!
if ('A' <= *i && *i <= 'Z') *i += 'a' - 'A';
}
}
inline void UpperString(string * s) {
string::iterator end = s->end();
for (string::iterator i = s->begin(); i != end; ++i) {
// toupper() changes based on locale. We don't want this!
if ('a' <= *i && *i <= 'z') *i += 'A' - 'a';
}
}
// ----------------------------------------------------------------------
// StringReplace()
// Give me a string and two patterns "old" and "new", and I replace
// the first instance of "old" in the string with "new", if it
// exists. RETURN a new string, regardless of whether the replacement
// happened or not.
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT string StringReplace(const string& s, const string& oldsub,
const string& newsub, bool replace_all);
// ----------------------------------------------------------------------
// SplitStringUsing()
// Split a string using a character delimiter. Append the components
// to 'result'. If there are consecutive delimiters, this function skips
// over all of them.
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT void SplitStringUsing(const string& full, const char* delim,
vector<string>* res);
// ----------------------------------------------------------------------
// JoinStrings()
// These methods concatenate a vector of strings into a C++ string, using
// the C-string "delim" as a separator between components. There are two
// flavors of the function, one flavor returns the concatenated string,
// another takes a pointer to the target string. In the latter case the
// target string is cleared and overwritten.
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT void JoinStrings(const vector<string>& components,
const char* delim, string* result);
inline string JoinStrings(const vector<string>& components,
const char* delim) {
string result;
JoinStrings(components, delim, &result);
return result;
}
// ----------------------------------------------------------------------
// UnescapeCEscapeSequences()
// Copies "source" to "dest", rewriting C-style escape sequences
// -- '\n', '\r', '\\', '\ooo', etc -- to their ASCII
// equivalents. "dest" must be sufficiently large to hold all
// the characters in the rewritten string (i.e. at least as large
// as strlen(source) + 1 should be safe, since the replacements
// are always shorter than the original escaped sequences). It's
// safe for source and dest to be the same. RETURNS the length
// of dest.
//
// It allows hex sequences \xhh, or generally \xhhhhh with an
// arbitrary number of hex digits, but all of them together must
// specify a value of a single byte (e.g. \x0045 is equivalent
// to \x45, and \x1234 is erroneous).
//
// It also allows escape sequences of the form \uhhhh (exactly four
// hex digits, upper or lower case) or \Uhhhhhhhh (exactly eight
// hex digits, upper or lower case) to specify a Unicode code
// point. The dest array will contain the UTF8-encoded version of
// that code-point (e.g., if source contains \u2019, then dest will
// contain the three bytes 0xE2, 0x80, and 0x99). For the inverse
// transformation, use UniLib::UTF8EscapeString
// (util/utf8/unilib.h), not CEscapeString.
//
// Errors: In the first form of the call, errors are reported with
// LOG(ERROR). The same is true for the second form of the call if
// the pointer to the string vector is NULL; otherwise, error
// messages are stored in the vector. In either case, the effect on
// the dest array is not defined, but rest of the source will be
// processed.
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT int UnescapeCEscapeSequences(const char* source, char* dest);
LIBPROTOBUF_EXPORT int UnescapeCEscapeSequences(const char* source, char* dest,
vector<string> *errors);
// ----------------------------------------------------------------------
// UnescapeCEscapeString()
// This does the same thing as UnescapeCEscapeSequences, but creates
// a new string. The caller does not need to worry about allocating
// a dest buffer. This should be used for non performance critical
// tasks such as printing debug messages. It is safe for src and dest
// to be the same.
//
// The second call stores its errors in a supplied string vector.
// If the string vector pointer is NULL, it reports the errors with LOG().
//
// In the first and second calls, the length of dest is returned. In the
// the third call, the new string is returned.
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT int UnescapeCEscapeString(const string& src, string* dest);
LIBPROTOBUF_EXPORT int UnescapeCEscapeString(const string& src, string* dest,
vector<string> *errors);
LIBPROTOBUF_EXPORT string UnescapeCEscapeString(const string& src);
// ----------------------------------------------------------------------
// CEscapeString()
// Copies 'src' to 'dest', escaping dangerous characters using
// C-style escape sequences. This is very useful for preparing query
// flags. 'src' and 'dest' should not overlap.
// Returns the number of bytes written to 'dest' (not including the \0)
// or -1 if there was insufficient space.
//
// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped.
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT int CEscapeString(const char* src, int src_len,
char* dest, int dest_len);
// ----------------------------------------------------------------------
// CEscape()
// More convenient form of CEscapeString: returns result as a "string".
// This version is slower than CEscapeString() because it does more
// allocation. However, it is much more convenient to use in
// non-speed-critical code like logging messages etc.
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT string CEscape(const string& src);
namespace strings {
// Like CEscape() but does not escape bytes with the upper bit set.
LIBPROTOBUF_EXPORT string Utf8SafeCEscape(const string& src);
// Like CEscape() but uses hex (\x) escapes instead of octals.
LIBPROTOBUF_EXPORT string CHexEscape(const string& src);
} // namespace strings
// ----------------------------------------------------------------------
// strto32()
// strtou32()
// strto64()
// strtou64()
// Architecture-neutral plug compatible replacements for strtol() and
// strtoul(). Long's have different lengths on ILP-32 and LP-64
// platforms, so using these is safer, from the point of view of
// overflow behavior, than using the standard libc functions.
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT int32 strto32_adaptor(const char *nptr, char **endptr,
int base);
LIBPROTOBUF_EXPORT uint32 strtou32_adaptor(const char *nptr, char **endptr,
int base);
inline int32 strto32(const char *nptr, char **endptr, int base) {
if (sizeof(int32) == sizeof(long))
return strtol(nptr, endptr, base);
else
return strto32_adaptor(nptr, endptr, base);
}
inline uint32 strtou32(const char *nptr, char **endptr, int base) {
if (sizeof(uint32) == sizeof(unsigned long))
return strtoul(nptr, endptr, base);
else
return strtou32_adaptor(nptr, endptr, base);
}
// For now, long long is 64-bit on all the platforms we care about, so these
// functions can simply pass the call to strto[u]ll.
inline int64 strto64(const char *nptr, char **endptr, int base) {
GOOGLE_COMPILE_ASSERT(sizeof(int64) == sizeof(long long),
sizeof_int64_is_not_sizeof_long_long);
return strtoll(nptr, endptr, base);
}
inline uint64 strtou64(const char *nptr, char **endptr, int base) {
GOOGLE_COMPILE_ASSERT(sizeof(uint64) == sizeof(unsigned long long),
sizeof_uint64_is_not_sizeof_long_long);
return strtoull(nptr, endptr, base);
}
// ----------------------------------------------------------------------
// FastIntToBuffer()
// FastHexToBuffer()
// FastHex64ToBuffer()
// FastHex32ToBuffer()
// FastTimeToBuffer()
// These are intended for speed. FastIntToBuffer() assumes the
// integer is non-negative. FastHexToBuffer() puts output in
// hex rather than decimal. FastTimeToBuffer() puts the output
// into RFC822 format.
//
// FastHex64ToBuffer() puts a 64-bit unsigned value in hex-format,
// padded to exactly 16 bytes (plus one byte for '\0')
//
// FastHex32ToBuffer() puts a 32-bit unsigned value in hex-format,
// padded to exactly 8 bytes (plus one byte for '\0')
//
// All functions take the output buffer as an arg.
// They all return a pointer to the beginning of the output,
// which may not be the beginning of the input buffer.
// ----------------------------------------------------------------------
// Suggested buffer size for FastToBuffer functions. Also works with
// DoubleToBuffer() and FloatToBuffer().
static const int kFastToBufferSize = 32;
LIBPROTOBUF_EXPORT char* FastInt32ToBuffer(int32 i, char* buffer);
LIBPROTOBUF_EXPORT char* FastInt64ToBuffer(int64 i, char* buffer);
char* FastUInt32ToBuffer(uint32 i, char* buffer); // inline below
char* FastUInt64ToBuffer(uint64 i, char* buffer); // inline below
LIBPROTOBUF_EXPORT char* FastHexToBuffer(int i, char* buffer);
LIBPROTOBUF_EXPORT char* FastHex64ToBuffer(uint64 i, char* buffer);
LIBPROTOBUF_EXPORT char* FastHex32ToBuffer(uint32 i, char* buffer);
// at least 22 bytes long
inline char* FastIntToBuffer(int i, char* buffer) {
return (sizeof(i) == 4 ?
FastInt32ToBuffer(i, buffer) : FastInt64ToBuffer(i, buffer));
}
inline char* FastUIntToBuffer(unsigned int i, char* buffer) {
return (sizeof(i) == 4 ?
FastUInt32ToBuffer(i, buffer) : FastUInt64ToBuffer(i, buffer));
}
inline char* FastLongToBuffer(long i, char* buffer) {
return (sizeof(i) == 4 ?
FastInt32ToBuffer(i, buffer) : FastInt64ToBuffer(i, buffer));
}
inline char* FastULongToBuffer(unsigned long i, char* buffer) {
return (sizeof(i) == 4 ?
FastUInt32ToBuffer(i, buffer) : FastUInt64ToBuffer(i, buffer));
}
// ----------------------------------------------------------------------
// FastInt32ToBufferLeft()
// FastUInt32ToBufferLeft()
// FastInt64ToBufferLeft()
// FastUInt64ToBufferLeft()
//
// Like the Fast*ToBuffer() functions above, these are intended for speed.
// Unlike the Fast*ToBuffer() functions, however, these functions write
// their output to the beginning of the buffer (hence the name, as the
// output is left-aligned). The caller is responsible for ensuring that
// the buffer has enough space to hold the output.
//
// Returns a pointer to the end of the string (i.e. the null character
// terminating the string).
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT char* FastInt32ToBufferLeft(int32 i, char* buffer);
LIBPROTOBUF_EXPORT char* FastUInt32ToBufferLeft(uint32 i, char* buffer);
LIBPROTOBUF_EXPORT char* FastInt64ToBufferLeft(int64 i, char* buffer);
LIBPROTOBUF_EXPORT char* FastUInt64ToBufferLeft(uint64 i, char* buffer);
// Just define these in terms of the above.
inline char* FastUInt32ToBuffer(uint32 i, char* buffer) {
FastUInt32ToBufferLeft(i, buffer);
return buffer;
}
inline char* FastUInt64ToBuffer(uint64 i, char* buffer) {
FastUInt64ToBufferLeft(i, buffer);
return buffer;
}
// ----------------------------------------------------------------------
// SimpleItoa()
// Description: converts an integer to a string.
//
// Return value: string
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT string SimpleItoa(int i);
LIBPROTOBUF_EXPORT string SimpleItoa(unsigned int i);
LIBPROTOBUF_EXPORT string SimpleItoa(long i);
LIBPROTOBUF_EXPORT string SimpleItoa(unsigned long i);
LIBPROTOBUF_EXPORT string SimpleItoa(long long i);
LIBPROTOBUF_EXPORT string SimpleItoa(unsigned long long i);
// ----------------------------------------------------------------------
// SimpleDtoa()
// SimpleFtoa()
// DoubleToBuffer()
// FloatToBuffer()
// Description: converts a double or float to a string which, if
// passed to NoLocaleStrtod(), will produce the exact same original double
// (except in case of NaN; all NaNs are considered the same value).
// We try to keep the string short but it's not guaranteed to be as
// short as possible.
//
// DoubleToBuffer() and FloatToBuffer() write the text to the given
// buffer and return it. The buffer must be at least
// kDoubleToBufferSize bytes for doubles and kFloatToBufferSize
// bytes for floats. kFastToBufferSize is also guaranteed to be large
// enough to hold either.
//
// Return value: string
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT string SimpleDtoa(double value);
LIBPROTOBUF_EXPORT string SimpleFtoa(float value);
LIBPROTOBUF_EXPORT char* DoubleToBuffer(double i, char* buffer);
LIBPROTOBUF_EXPORT char* FloatToBuffer(float i, char* buffer);
// In practice, doubles should never need more than 24 bytes and floats
// should never need more than 14 (including null terminators), but we
// overestimate to be safe.
static const int kDoubleToBufferSize = 32;
static const int kFloatToBufferSize = 24;
// ----------------------------------------------------------------------
// NoLocaleStrtod()
// Exactly like strtod(), except it always behaves as if in the "C"
// locale (i.e. decimal points must be '.'s).
// ----------------------------------------------------------------------
LIBPROTOBUF_EXPORT double NoLocaleStrtod(const char* text, char** endptr);
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_STUBS_STRUTIL_H__

View file

@ -0,0 +1,83 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <locale.h>
namespace google {
namespace protobuf {
namespace {
// TODO(kenton): Copy strutil tests from google3?
TEST(StringUtilityTest, ImmuneToLocales) {
// Remember the old locale.
char* old_locale_cstr = setlocale(LC_NUMERIC, NULL);
ASSERT_TRUE(old_locale_cstr != NULL);
string old_locale = old_locale_cstr;
// Set the locale to "C".
ASSERT_TRUE(setlocale(LC_NUMERIC, "C") != NULL);
EXPECT_EQ(1.5, NoLocaleStrtod("1.5", NULL));
EXPECT_EQ("1.5", SimpleDtoa(1.5));
EXPECT_EQ("1.5", SimpleFtoa(1.5));
// Verify that the endptr is set correctly even if not all text was parsed.
const char* text = "1.5f";
char* endptr;
EXPECT_EQ(1.5, NoLocaleStrtod(text, &endptr));
EXPECT_EQ(3, endptr - text);
if (setlocale(LC_NUMERIC, "es_ES") == NULL &&
setlocale(LC_NUMERIC, "es_ES.utf8") == NULL) {
// Some systems may not have the desired locale available.
GOOGLE_LOG(WARNING)
<< "Couldn't set locale to es_ES. Skipping this test.";
} else {
EXPECT_EQ(1.5, NoLocaleStrtod("1.5", NULL));
EXPECT_EQ("1.5", SimpleDtoa(1.5));
EXPECT_EQ("1.5", SimpleFtoa(1.5));
EXPECT_EQ(1.5, NoLocaleStrtod(text, &endptr));
EXPECT_EQ(3, endptr - text);
}
// Return to original locale.
setlocale(LC_NUMERIC, old_locale.c_str());
}
} // anonymous namespace
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,134 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
namespace protobuf {
namespace strings {
using internal::SubstituteArg;
// Returns the number of args in arg_array which were passed explicitly
// to Substitute().
static int CountSubstituteArgs(const SubstituteArg* const* args_array) {
int count = 0;
while (args_array[count] != NULL && args_array[count]->size() != -1) {
++count;
}
return count;
}
string Substitute(
const char* format,
const SubstituteArg& arg0, const SubstituteArg& arg1,
const SubstituteArg& arg2, const SubstituteArg& arg3,
const SubstituteArg& arg4, const SubstituteArg& arg5,
const SubstituteArg& arg6, const SubstituteArg& arg7,
const SubstituteArg& arg8, const SubstituteArg& arg9) {
string result;
SubstituteAndAppend(&result, format, arg0, arg1, arg2, arg3, arg4,
arg5, arg6, arg7, arg8, arg9);
return result;
}
void SubstituteAndAppend(
string* output, const char* format,
const SubstituteArg& arg0, const SubstituteArg& arg1,
const SubstituteArg& arg2, const SubstituteArg& arg3,
const SubstituteArg& arg4, const SubstituteArg& arg5,
const SubstituteArg& arg6, const SubstituteArg& arg7,
const SubstituteArg& arg8, const SubstituteArg& arg9) {
const SubstituteArg* const args_array[] = {
&arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, NULL
};
// Determine total size needed.
int size = 0;
for (int i = 0; format[i] != '\0'; i++) {
if (format[i] == '$') {
if (ascii_isdigit(format[i+1])) {
int index = format[i+1] - '0';
if (args_array[index]->size() == -1) {
GOOGLE_LOG(DFATAL)
<< "strings::Substitute format string invalid: asked for \"$"
<< index << "\", but only " << CountSubstituteArgs(args_array)
<< " args were given. Full format string was: \""
<< CEscape(format) << "\".";
return;
}
size += args_array[index]->size();
++i; // Skip next char.
} else if (format[i+1] == '$') {
++size;
++i; // Skip next char.
} else {
GOOGLE_LOG(DFATAL)
<< "Invalid strings::Substitute() format string: \""
<< CEscape(format) << "\".";
return;
}
} else {
++size;
}
}
if (size == 0) return;
// Build the string.
int original_size = output->size();
STLStringResizeUninitialized(output, original_size + size);
char* target = string_as_array(output) + original_size;
for (int i = 0; format[i] != '\0'; i++) {
if (format[i] == '$') {
if (ascii_isdigit(format[i+1])) {
const SubstituteArg* src = args_array[format[i+1] - '0'];
memcpy(target, src->data(), src->size());
target += src->size();
++i; // Skip next char.
} else if (format[i+1] == '$') {
*target++ = '$';
++i; // Skip next char.
}
} else {
*target++ = format[i];
}
}
GOOGLE_DCHECK_EQ(target - output->data(), output->size());
}
} // namespace strings
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,170 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// from google3/strings/substitute.h
#include <string>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>
#ifndef GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_
#define GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_
namespace google {
namespace protobuf {
namespace strings {
// ----------------------------------------------------------------------
// strings::Substitute()
// strings::SubstituteAndAppend()
// Kind of like StringPrintf, but different.
//
// Example:
// string GetMessage(string first_name, string last_name, int age) {
// return strings::Substitute("My name is $0 $1 and I am $2 years old.",
// first_name, last_name, age);
// }
//
// Differences from StringPrintf:
// * The format string does not identify the types of arguments.
// Instead, the magic of C++ deals with this for us. See below
// for a list of accepted types.
// * Substitutions in the format string are identified by a '$'
// followed by a digit. So, you can use arguments out-of-order and
// use the same argument multiple times.
// * It's much faster than StringPrintf.
//
// Supported types:
// * Strings (const char*, const string&)
// * Note that this means you do not have to add .c_str() to all of
// your strings. In fact, you shouldn't; it will be slower.
// * int32, int64, uint32, uint64: Formatted using SimpleItoa().
// * float, double: Formatted using SimpleFtoa() and SimpleDtoa().
// * bool: Printed as "true" or "false".
//
// SubstituteAndAppend() is like Substitute() but appends the result to
// *output. Example:
//
// string str;
// strings::SubstituteAndAppend(&str,
// "My name is $0 $1 and I am $2 years old.",
// first_name, last_name, age);
//
// Substitute() is significantly faster than StringPrintf(). For very
// large strings, it may be orders of magnitude faster.
// ----------------------------------------------------------------------
namespace internal { // Implementation details.
class SubstituteArg {
public:
inline SubstituteArg(const char* value)
: text_(value), size_(strlen(text_)) {}
inline SubstituteArg(const string& value)
: text_(value.data()), size_(value.size()) {}
// Indicates that no argument was given.
inline explicit SubstituteArg()
: text_(NULL), size_(-1) {}
// Primitives
// We don't overload for signed and unsigned char because if people are
// explicitly declaring their chars as signed or unsigned then they are
// probably actually using them as 8-bit integers and would probably
// prefer an integer representation. But, we don't really know. So, we
// make the caller decide what to do.
inline SubstituteArg(char value)
: text_(scratch_), size_(1) { scratch_[0] = value; }
inline SubstituteArg(short value)
: text_(FastInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(unsigned short value)
: text_(FastUInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(int value)
: text_(FastInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(unsigned int value)
: text_(FastUInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(long value)
: text_(FastLongToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(unsigned long value)
: text_(FastULongToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(long long value)
: text_(FastInt64ToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(unsigned long long value)
: text_(FastUInt64ToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(float value)
: text_(FloatToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(double value)
: text_(DoubleToBuffer(value, scratch_)), size_(strlen(text_)) {}
inline SubstituteArg(bool value)
: text_(value ? "true" : "false"), size_(strlen(text_)) {}
inline const char* data() const { return text_; }
inline int size() const { return size_; }
private:
const char* text_;
int size_;
char scratch_[kFastToBufferSize];
};
} // namespace internal
LIBPROTOBUF_EXPORT string Substitute(
const char* format,
const internal::SubstituteArg& arg0 = internal::SubstituteArg(),
const internal::SubstituteArg& arg1 = internal::SubstituteArg(),
const internal::SubstituteArg& arg2 = internal::SubstituteArg(),
const internal::SubstituteArg& arg3 = internal::SubstituteArg(),
const internal::SubstituteArg& arg4 = internal::SubstituteArg(),
const internal::SubstituteArg& arg5 = internal::SubstituteArg(),
const internal::SubstituteArg& arg6 = internal::SubstituteArg(),
const internal::SubstituteArg& arg7 = internal::SubstituteArg(),
const internal::SubstituteArg& arg8 = internal::SubstituteArg(),
const internal::SubstituteArg& arg9 = internal::SubstituteArg());
LIBPROTOBUF_EXPORT void SubstituteAndAppend(
string* output, const char* format,
const internal::SubstituteArg& arg0 = internal::SubstituteArg(),
const internal::SubstituteArg& arg1 = internal::SubstituteArg(),
const internal::SubstituteArg& arg2 = internal::SubstituteArg(),
const internal::SubstituteArg& arg3 = internal::SubstituteArg(),
const internal::SubstituteArg& arg4 = internal::SubstituteArg(),
const internal::SubstituteArg& arg5 = internal::SubstituteArg(),
const internal::SubstituteArg& arg6 = internal::SubstituteArg(),
const internal::SubstituteArg& arg7 = internal::SubstituteArg(),
const internal::SubstituteArg& arg8 = internal::SubstituteArg(),
const internal::SubstituteArg& arg9 = internal::SubstituteArg());
} // namespace strings
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,174 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_TEST_UTIL_H__
#define GOOGLE_PROTOBUF_TEST_UTIL_H__
#include <stack>
#include <string>
#include <google/protobuf/message.h>
#include <google/protobuf/unittest.pb.h>
namespace google {
namespace protobuf {
namespace unittest = protobuf_unittest;
namespace unittest_import = protobuf_unittest_import;
class TestUtil {
public:
// Set every field in the message to a unique value.
static void SetAllFields(unittest::TestAllTypes* message);
static void SetAllExtensions(unittest::TestAllExtensions* message);
static void SetAllFieldsAndExtensions(unittest::TestFieldOrderings* message);
static void SetPackedFields(unittest::TestPackedTypes* message);
static void SetPackedExtensions(unittest::TestPackedExtensions* message);
static void SetUnpackedFields(unittest::TestUnpackedTypes* message);
// Use the repeated versions of the set_*() accessors to modify all the
// repeated fields of the messsage (which should already have been
// initialized with Set*Fields()). Set*Fields() itself only tests
// the add_*() accessors.
static void ModifyRepeatedFields(unittest::TestAllTypes* message);
static void ModifyRepeatedExtensions(unittest::TestAllExtensions* message);
static void ModifyPackedFields(unittest::TestPackedTypes* message);
static void ModifyPackedExtensions(unittest::TestPackedExtensions* message);
// Check that all fields have the values that they should have after
// Set*Fields() is called.
static void ExpectAllFieldsSet(const unittest::TestAllTypes& message);
static void ExpectAllExtensionsSet(
const unittest::TestAllExtensions& message);
static void ExpectPackedFieldsSet(const unittest::TestPackedTypes& message);
static void ExpectPackedExtensionsSet(
const unittest::TestPackedExtensions& message);
static void ExpectUnpackedFieldsSet(
const unittest::TestUnpackedTypes& message);
// Expect that the message is modified as would be expected from
// Modify*Fields().
static void ExpectRepeatedFieldsModified(
const unittest::TestAllTypes& message);
static void ExpectRepeatedExtensionsModified(
const unittest::TestAllExtensions& message);
static void ExpectPackedFieldsModified(
const unittest::TestPackedTypes& message);
static void ExpectPackedExtensionsModified(
const unittest::TestPackedExtensions& message);
// Check that all fields have their default values.
static void ExpectClear(const unittest::TestAllTypes& message);
static void ExpectExtensionsClear(const unittest::TestAllExtensions& message);
static void ExpectPackedClear(const unittest::TestPackedTypes& message);
static void ExpectPackedExtensionsClear(
const unittest::TestPackedExtensions& message);
// Check that the passed-in serialization is the canonical serialization we
// expect for a TestFieldOrderings message filled in by
// SetAllFieldsAndExtensions().
static void ExpectAllFieldsAndExtensionsInOrder(const string& serialized);
// Check that all repeated fields have had their last elements removed.
static void ExpectLastRepeatedsRemoved(
const unittest::TestAllTypes& message);
static void ExpectLastRepeatedExtensionsRemoved(
const unittest::TestAllExtensions& message);
// Check that all repeated fields have had their first and last elements
// swapped.
static void ExpectRepeatedsSwapped(const unittest::TestAllTypes& message);
static void ExpectRepeatedExtensionsSwapped(
const unittest::TestAllExtensions& message);
// Like above, but use the reflection interface.
class ReflectionTester {
public:
// base_descriptor must be a descriptor for TestAllTypes or
// TestAllExtensions. In the former case, ReflectionTester fetches from
// it the FieldDescriptors needed to use the reflection interface. In
// the latter case, ReflectionTester searches for extension fields in
// its file.
explicit ReflectionTester(const Descriptor* base_descriptor);
void SetAllFieldsViaReflection(Message* message);
void ModifyRepeatedFieldsViaReflection(Message* message);
void ExpectAllFieldsSetViaReflection(const Message& message);
void ExpectClearViaReflection(const Message& message);
void SetPackedFieldsViaReflection(Message* message);
void ModifyPackedFieldsViaReflection(Message* message);
void ExpectPackedFieldsSetViaReflection(const Message& message);
void ExpectPackedClearViaReflection(const Message& message);
void RemoveLastRepeatedsViaReflection(Message* message);
void SwapRepeatedsViaReflection(Message* message);
private:
const FieldDescriptor* F(const string& name);
const Descriptor* base_descriptor_;
const FieldDescriptor* group_a_;
const FieldDescriptor* repeated_group_a_;
const FieldDescriptor* nested_b_;
const FieldDescriptor* foreign_c_;
const FieldDescriptor* import_d_;
const EnumValueDescriptor* nested_foo_;
const EnumValueDescriptor* nested_bar_;
const EnumValueDescriptor* nested_baz_;
const EnumValueDescriptor* foreign_foo_;
const EnumValueDescriptor* foreign_bar_;
const EnumValueDescriptor* foreign_baz_;
const EnumValueDescriptor* import_foo_;
const EnumValueDescriptor* import_bar_;
const EnumValueDescriptor* import_baz_;
// We have to split this into three function otherwise it creates a stack
// frame so large that it triggers a warning.
void ExpectAllFieldsSetViaReflection1(const Message& message);
void ExpectAllFieldsSetViaReflection2(const Message& message);
void ExpectAllFieldsSetViaReflection3(const Message& message);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ReflectionTester);
};
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TestUtil);
};
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_TEST_UTIL_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,101 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_TEST_UTIL_LITE_H__
#define GOOGLE_PROTOBUF_TEST_UTIL_LITE_H__
#include <google/protobuf/unittest_lite.pb.h>
namespace google {
namespace protobuf {
namespace unittest = protobuf_unittest;
namespace unittest_import = protobuf_unittest_import;
class TestUtilLite {
public:
// Set every field in the message to a unique value.
static void SetAllFields(unittest::TestAllTypesLite* message);
static void SetAllExtensions(unittest::TestAllExtensionsLite* message);
static void SetPackedFields(unittest::TestPackedTypesLite* message);
static void SetPackedExtensions(unittest::TestPackedExtensionsLite* message);
// Use the repeated versions of the set_*() accessors to modify all the
// repeated fields of the messsage (which should already have been
// initialized with Set*Fields()). Set*Fields() itself only tests
// the add_*() accessors.
static void ModifyRepeatedFields(unittest::TestAllTypesLite* message);
static void ModifyRepeatedExtensions(
unittest::TestAllExtensionsLite* message);
static void ModifyPackedFields(unittest::TestPackedTypesLite* message);
static void ModifyPackedExtensions(
unittest::TestPackedExtensionsLite* message);
// Check that all fields have the values that they should have after
// Set*Fields() is called.
static void ExpectAllFieldsSet(const unittest::TestAllTypesLite& message);
static void ExpectAllExtensionsSet(
const unittest::TestAllExtensionsLite& message);
static void ExpectPackedFieldsSet(
const unittest::TestPackedTypesLite& message);
static void ExpectPackedExtensionsSet(
const unittest::TestPackedExtensionsLite& message);
// Expect that the message is modified as would be expected from
// Modify*Fields().
static void ExpectRepeatedFieldsModified(
const unittest::TestAllTypesLite& message);
static void ExpectRepeatedExtensionsModified(
const unittest::TestAllExtensionsLite& message);
static void ExpectPackedFieldsModified(
const unittest::TestPackedTypesLite& message);
static void ExpectPackedExtensionsModified(
const unittest::TestPackedExtensionsLite& message);
// Check that all fields have their default values.
static void ExpectClear(const unittest::TestAllTypesLite& message);
static void ExpectExtensionsClear(
const unittest::TestAllExtensionsLite& message);
static void ExpectPackedClear(const unittest::TestPackedTypesLite& message);
static void ExpectPackedExtensionsClear(
const unittest::TestPackedExtensionsLite& message);
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TestUtilLite);
};
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_TEST_UTIL_LITE_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,264 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: jschorr@google.com (Joseph Schorr)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Utilities for printing and parsing protocol messages in a human-readable,
// text-based format.
#ifndef GOOGLE_PROTOBUF_TEXT_FORMAT_H__
#define GOOGLE_PROTOBUF_TEXT_FORMAT_H__
#include <string>
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
namespace google {
namespace protobuf {
namespace io {
class ErrorCollector; // tokenizer.h
}
// This class implements protocol buffer text format. Printing and parsing
// protocol messages in text format is useful for debugging and human editing
// of messages.
//
// This class is really a namespace that contains only static methods.
class LIBPROTOBUF_EXPORT TextFormat {
public:
// Outputs a textual representation of the given message to the given
// output stream.
static bool Print(const Message& message, io::ZeroCopyOutputStream* output);
// Print the fields in an UnknownFieldSet. They are printed by tag number
// only. Embedded messages are heuristically identified by attempting to
// parse them.
static bool PrintUnknownFields(const UnknownFieldSet& unknown_fields,
io::ZeroCopyOutputStream* output);
// Like Print(), but outputs directly to a string.
static bool PrintToString(const Message& message, string* output);
// Like PrintUnknownFields(), but outputs directly to a string.
static bool PrintUnknownFieldsToString(const UnknownFieldSet& unknown_fields,
string* output);
// Outputs a textual representation of the value of the field supplied on
// the message supplied. For non-repeated fields, an index of -1 must
// be supplied. Note that this method will print the default value for a
// field if it is not set.
static void PrintFieldValueToString(const Message& message,
const FieldDescriptor* field,
int index,
string* output);
// Class for those users which require more fine-grained control over how
// a protobuffer message is printed out.
class LIBPROTOBUF_EXPORT Printer {
public:
Printer();
~Printer();
// Like TextFormat::Print
bool Print(const Message& message, io::ZeroCopyOutputStream* output);
// Like TextFormat::PrintUnknownFields
bool PrintUnknownFields(const UnknownFieldSet& unknown_fields,
io::ZeroCopyOutputStream* output);
// Like TextFormat::PrintToString
bool PrintToString(const Message& message, string* output);
// Like TextFormat::PrintUnknownFieldsToString
bool PrintUnknownFieldsToString(const UnknownFieldSet& unknown_fields,
string* output);
// Like TextFormat::PrintFieldValueToString
void PrintFieldValueToString(const Message& message,
const FieldDescriptor* field,
int index,
string* output);
// Adjust the initial indent level of all output. Each indent level is
// equal to two spaces.
void SetInitialIndentLevel(int indent_level) {
initial_indent_level_ = indent_level;
}
// If printing in single line mode, then the entire message will be output
// on a single line with no line breaks.
void SetSingleLineMode(bool single_line_mode) {
single_line_mode_ = single_line_mode;
}
// Set true to print repeated primitives in a format like:
// field_name: [1, 2, 3, 4]
// instead of printing each value on its own line. Short format applies
// only to primitive values -- i.e. everything except strings and
// sub-messages/groups. Note that at present this format is not recognized
// by the parser.
void SetUseShortRepeatedPrimitives(bool use_short_repeated_primitives) {
use_short_repeated_primitives_ = use_short_repeated_primitives;
}
// Set true to output UTF-8 instead of ASCII. The only difference
// is that bytes >= 0x80 in string fields will not be escaped,
// because they are assumed to be part of UTF-8 multi-byte
// sequences.
void SetUseUtf8StringEscaping(bool as_utf8) {
utf8_string_escaping_ = as_utf8;
}
private:
// Forward declaration of an internal class used to print the text
// output to the OutputStream (see text_format.cc for implementation).
class TextGenerator;
// Internal Print method, used for writing to the OutputStream via
// the TextGenerator class.
void Print(const Message& message,
TextGenerator& generator);
// Print a single field.
void PrintField(const Message& message,
const Reflection* reflection,
const FieldDescriptor* field,
TextGenerator& generator);
// Print a repeated primitive field in short form.
void PrintShortRepeatedField(const Message& message,
const Reflection* reflection,
const FieldDescriptor* field,
TextGenerator& generator);
// Print the name of a field -- i.e. everything that comes before the
// ':' for a single name/value pair.
void PrintFieldName(const Message& message,
const Reflection* reflection,
const FieldDescriptor* field,
TextGenerator& generator);
// Outputs a textual representation of the value of the field supplied on
// the message supplied or the default value if not set.
void PrintFieldValue(const Message& message,
const Reflection* reflection,
const FieldDescriptor* field,
int index,
TextGenerator& generator);
// Print the fields in an UnknownFieldSet. They are printed by tag number
// only. Embedded messages are heuristically identified by attempting to
// parse them.
void PrintUnknownFields(const UnknownFieldSet& unknown_fields,
TextGenerator& generator);
int initial_indent_level_;
bool single_line_mode_;
bool use_short_repeated_primitives_;
bool utf8_string_escaping_;
};
// Parses a text-format protocol message from the given input stream to
// the given message object. This function parses the format written
// by Print().
static bool Parse(io::ZeroCopyInputStream* input, Message* output);
// Like Parse(), but reads directly from a string.
static bool ParseFromString(const string& input, Message* output);
// Like Parse(), but the data is merged into the given message, as if
// using Message::MergeFrom().
static bool Merge(io::ZeroCopyInputStream* input, Message* output);
// Like Merge(), but reads directly from a string.
static bool MergeFromString(const string& input, Message* output);
// Parse the given text as a single field value and store it into the
// given field of the given message. If the field is a repeated field,
// the new value will be added to the end
static bool ParseFieldValueFromString(const string& input,
const FieldDescriptor* field,
Message* message);
// For more control over parsing, use this class.
class LIBPROTOBUF_EXPORT Parser {
public:
Parser();
~Parser();
// Like TextFormat::Parse().
bool Parse(io::ZeroCopyInputStream* input, Message* output);
// Like TextFormat::ParseFromString().
bool ParseFromString(const string& input, Message* output);
// Like TextFormat::Merge().
bool Merge(io::ZeroCopyInputStream* input, Message* output);
// Like TextFormat::MergeFromString().
bool MergeFromString(const string& input, Message* output);
// Set where to report parse errors. If NULL (the default), errors will
// be printed to stderr.
void RecordErrorsTo(io::ErrorCollector* error_collector) {
error_collector_ = error_collector;
}
// Normally parsing fails if, after parsing, output->IsInitialized()
// returns false. Call AllowPartialMessage(true) to skip this check.
void AllowPartialMessage(bool allow) {
allow_partial_ = allow;
}
// Like TextFormat::ParseFieldValueFromString
bool ParseFieldValueFromString(const string& input,
const FieldDescriptor* field,
Message* output);
private:
// Forward declaration of an internal class used to parse text
// representations (see text_format.cc for implementation).
class ParserImpl;
// Like TextFormat::Merge(). The provided implementation is used
// to do the parsing.
bool MergeUsingImpl(io::ZeroCopyInputStream* input,
Message* output,
ParserImpl* parser_impl);
io::ErrorCollector* error_collector_;
bool allow_partial_;
};
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TextFormat);
};
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_TEXT_FORMAT_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,204 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/unknown_field_set.h>
#include <google/protobuf/stubs/stl_util-inl.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/wire_format.h>
namespace google {
namespace protobuf {
UnknownFieldSet::UnknownFieldSet()
: fields_(NULL) {}
UnknownFieldSet::~UnknownFieldSet() {
Clear();
delete fields_;
}
void UnknownFieldSet::ClearFallback() {
GOOGLE_DCHECK(fields_ != NULL);
for (int i = 0; i < fields_->size(); i++) {
(*fields_)[i].Delete();
}
fields_->clear();
}
void UnknownFieldSet::MergeFrom(const UnknownFieldSet& other) {
for (int i = 0; i < other.field_count(); i++) {
AddField(other.field(i));
}
}
int UnknownFieldSet::SpaceUsedExcludingSelf() const {
if (fields_ == NULL) return 0;
int total_size = sizeof(*fields_) + sizeof(UnknownField) * fields_->size();
for (int i = 0; i < fields_->size(); i++) {
const UnknownField& field = (*fields_)[i];
switch (field.type()) {
case UnknownField::TYPE_LENGTH_DELIMITED:
total_size += sizeof(*field.length_delimited_) +
internal::StringSpaceUsedExcludingSelf(*field.length_delimited_);
break;
case UnknownField::TYPE_GROUP:
total_size += field.group_->SpaceUsed();
break;
default:
break;
}
}
return total_size;
}
int UnknownFieldSet::SpaceUsed() const {
return sizeof(*this) + SpaceUsedExcludingSelf();
}
void UnknownFieldSet::AddVarint(int number, uint64 value) {
if (fields_ == NULL) fields_ = new vector<UnknownField>;
UnknownField field;
field.number_ = number;
field.type_ = UnknownField::TYPE_VARINT;
field.varint_ = value;
fields_->push_back(field);
}
void UnknownFieldSet::AddFixed32(int number, uint32 value) {
if (fields_ == NULL) fields_ = new vector<UnknownField>;
UnknownField field;
field.number_ = number;
field.type_ = UnknownField::TYPE_FIXED32;
field.fixed32_ = value;
fields_->push_back(field);
}
void UnknownFieldSet::AddFixed64(int number, uint64 value) {
if (fields_ == NULL) fields_ = new vector<UnknownField>;
UnknownField field;
field.number_ = number;
field.type_ = UnknownField::TYPE_FIXED64;
field.fixed64_ = value;
fields_->push_back(field);
}
string* UnknownFieldSet::AddLengthDelimited(int number) {
if (fields_ == NULL) fields_ = new vector<UnknownField>;
UnknownField field;
field.number_ = number;
field.type_ = UnknownField::TYPE_LENGTH_DELIMITED;
field.length_delimited_ = new string;
fields_->push_back(field);
return field.length_delimited_;
}
UnknownFieldSet* UnknownFieldSet::AddGroup(int number) {
if (fields_ == NULL) fields_ = new vector<UnknownField>;
UnknownField field;
field.number_ = number;
field.type_ = UnknownField::TYPE_GROUP;
field.group_ = new UnknownFieldSet;
fields_->push_back(field);
return field.group_;
}
void UnknownFieldSet::AddField(const UnknownField& field) {
if (fields_ == NULL) fields_ = new vector<UnknownField>;
fields_->push_back(field);
fields_->back().DeepCopy();
}
bool UnknownFieldSet::MergeFromCodedStream(io::CodedInputStream* input) {
UnknownFieldSet other;
if (internal::WireFormat::SkipMessage(input, &other) &&
input->ConsumedEntireMessage()) {
MergeFrom(other);
return true;
} else {
return false;
}
}
bool UnknownFieldSet::ParseFromCodedStream(io::CodedInputStream* input) {
Clear();
return MergeFromCodedStream(input);
}
bool UnknownFieldSet::ParseFromZeroCopyStream(io::ZeroCopyInputStream* input) {
io::CodedInputStream coded_input(input);
return ParseFromCodedStream(&coded_input) &&
coded_input.ConsumedEntireMessage();
}
bool UnknownFieldSet::ParseFromArray(const void* data, int size) {
io::ArrayInputStream input(data, size);
return ParseFromZeroCopyStream(&input);
}
void UnknownField::Delete() {
switch (type()) {
case UnknownField::TYPE_LENGTH_DELIMITED:
delete length_delimited_;
break;
case UnknownField::TYPE_GROUP:
delete group_;
break;
default:
break;
}
}
void UnknownField::DeepCopy() {
switch (type()) {
case UnknownField::TYPE_LENGTH_DELIMITED:
length_delimited_ = new string(*length_delimited_);
break;
case UnknownField::TYPE_GROUP: {
UnknownFieldSet* group = new UnknownFieldSet;
group->MergeFrom(*group_);
group_ = group;
break;
}
default:
break;
}
}
} // namespace protobuf
} // namespace google

View file

@ -0,0 +1,268 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Contains classes used to keep track of unrecognized fields seen while
// parsing a protocol message.
#ifndef GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__
#define GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__
#include <string>
#include <vector>
#include <google/protobuf/repeated_field.h>
namespace google {
namespace protobuf {
class Message; // message.h
class UnknownField; // below
// An UnknownFieldSet contains fields that were encountered while parsing a
// message but were not defined by its type. Keeping track of these can be
// useful, especially in that they may be written if the message is serialized
// again without being cleared in between. This means that software which
// simply receives messages and forwards them to other servers does not need
// to be updated every time a new field is added to the message definition.
//
// To get the UnknownFieldSet attached to any message, call
// Reflection::GetUnknownFields().
//
// This class is necessarily tied to the protocol buffer wire format, unlike
// the Reflection interface which is independent of any serialization scheme.
class LIBPROTOBUF_EXPORT UnknownFieldSet {
public:
UnknownFieldSet();
~UnknownFieldSet();
// Remove all fields.
inline void Clear();
// Is this set empty?
inline bool empty() const;
// Merge the contents of some other UnknownFieldSet with this one.
void MergeFrom(const UnknownFieldSet& other);
// Swaps the contents of some other UnknownFieldSet with this one.
inline void Swap(UnknownFieldSet* x);
// Computes (an estimate of) the total number of bytes currently used for
// storing the unknown fields in memory. Does NOT include
// sizeof(*this) in the calculation.
int SpaceUsedExcludingSelf() const;
// Version of SpaceUsed() including sizeof(*this).
int SpaceUsed() const;
// Returns the number of fields present in the UnknownFieldSet.
inline int field_count() const;
// Get a field in the set, where 0 <= index < field_count(). The fields
// appear in the order in which they were added.
inline const UnknownField& field(int index) const;
// Get a mutable pointer to a field in the set, where
// 0 <= index < field_count(). The fields appear in the order in which
// they were added.
inline UnknownField* mutable_field(int index);
// Adding fields ---------------------------------------------------
void AddVarint(int number, uint64 value);
void AddFixed32(int number, uint32 value);
void AddFixed64(int number, uint64 value);
void AddLengthDelimited(int number, const string& value);
string* AddLengthDelimited(int number);
UnknownFieldSet* AddGroup(int number);
// Adds an unknown field from another set.
void AddField(const UnknownField& field);
// Parsing helpers -------------------------------------------------
// These work exactly like the similarly-named methods of Message.
bool MergeFromCodedStream(io::CodedInputStream* input);
bool ParseFromCodedStream(io::CodedInputStream* input);
bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input);
bool ParseFromArray(const void* data, int size);
inline bool ParseFromString(const string& data) {
return ParseFromArray(data.data(), data.size());
}
private:
void ClearFallback();
vector<UnknownField>* fields_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UnknownFieldSet);
};
// Represents one field in an UnknownFieldSet.
class LIBPROTOBUF_EXPORT UnknownField {
public:
enum Type {
TYPE_VARINT,
TYPE_FIXED32,
TYPE_FIXED64,
TYPE_LENGTH_DELIMITED,
TYPE_GROUP
};
// The field's tag number, as seen on the wire.
inline int number() const;
// The field type.
inline Type type() const;
// Accessors -------------------------------------------------------
// Each method works only for UnknownFields of the corresponding type.
inline uint64 varint() const;
inline uint32 fixed32() const;
inline uint64 fixed64() const;
inline const string& length_delimited() const;
inline const UnknownFieldSet& group() const;
inline void set_varint(uint64 value);
inline void set_fixed32(uint32 value);
inline void set_fixed64(uint64 value);
inline void set_length_delimited(const string& value);
inline string* mutable_length_delimited();
inline UnknownFieldSet* mutable_group();
private:
friend class UnknownFieldSet;
// If this UnknownField contains a pointer, delete it.
void Delete();
// Make a deep copy of any pointers in this UnknownField.
void DeepCopy();
unsigned int number_ : 29;
unsigned int type_ : 3;
union {
uint64 varint_;
uint32 fixed32_;
uint64 fixed64_;
string* length_delimited_;
UnknownFieldSet* group_;
};
};
// ===================================================================
// inline implementations
inline void UnknownFieldSet::Clear() {
if (fields_ != NULL) {
ClearFallback();
}
}
inline bool UnknownFieldSet::empty() const {
return fields_ == NULL || fields_->empty();
}
inline void UnknownFieldSet::Swap(UnknownFieldSet* x) {
std::swap(fields_, x->fields_);
}
inline int UnknownFieldSet::field_count() const {
return (fields_ == NULL) ? 0 : fields_->size();
}
inline const UnknownField& UnknownFieldSet::field(int index) const {
return (*fields_)[index];
}
inline UnknownField* UnknownFieldSet::mutable_field(int index) {
return &(*fields_)[index];
}
inline void UnknownFieldSet::AddLengthDelimited(
int number, const string& value) {
AddLengthDelimited(number)->assign(value);
}
inline int UnknownField::number() const { return number_; }
inline UnknownField::Type UnknownField::type() const {
return static_cast<Type>(type_);
}
inline uint64 UnknownField::varint () const {
GOOGLE_DCHECK_EQ(type_, TYPE_VARINT);
return varint_;
}
inline uint32 UnknownField::fixed32() const {
GOOGLE_DCHECK_EQ(type_, TYPE_FIXED32);
return fixed32_;
}
inline uint64 UnknownField::fixed64() const {
GOOGLE_DCHECK_EQ(type_, TYPE_FIXED64);
return fixed64_;
}
inline const string& UnknownField::length_delimited() const {
GOOGLE_DCHECK_EQ(type_, TYPE_LENGTH_DELIMITED);
return *length_delimited_;
}
inline const UnknownFieldSet& UnknownField::group() const {
GOOGLE_DCHECK_EQ(type_, TYPE_GROUP);
return *group_;
}
inline void UnknownField::set_varint(uint64 value) {
GOOGLE_DCHECK_EQ(type_, TYPE_VARINT);
varint_ = value;
}
inline void UnknownField::set_fixed32(uint32 value) {
GOOGLE_DCHECK_EQ(type_, TYPE_FIXED32);
fixed32_ = value;
}
inline void UnknownField::set_fixed64(uint64 value) {
GOOGLE_DCHECK_EQ(type_, TYPE_FIXED64);
fixed64_ = value;
}
inline void UnknownField::set_length_delimited(const string& value) {
GOOGLE_DCHECK_EQ(type_, TYPE_LENGTH_DELIMITED);
length_delimited_->assign(value);
}
inline string* UnknownField::mutable_length_delimited() {
GOOGLE_DCHECK_EQ(type_, TYPE_LENGTH_DELIMITED);
return length_delimited_;
}
inline UnknownFieldSet* UnknownField::mutable_group() {
GOOGLE_DCHECK_EQ(type_, TYPE_GROUP);
return group_;
}
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__

View file

@ -0,0 +1,512 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This test is testing a lot more than just the UnknownFieldSet class. It
// tests handling of unknown fields throughout the system.
#include <google/protobuf/unknown_field_set.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/stl_util-inl.h>
namespace google {
namespace protobuf {
using internal::WireFormat;
namespace {
class UnknownFieldSetTest : public testing::Test {
protected:
virtual void SetUp() {
descriptor_ = unittest::TestAllTypes::descriptor();
TestUtil::SetAllFields(&all_fields_);
all_fields_.SerializeToString(&all_fields_data_);
ASSERT_TRUE(empty_message_.ParseFromString(all_fields_data_));
unknown_fields_ = empty_message_.mutable_unknown_fields();
}
const UnknownField* GetField(const string& name) {
const FieldDescriptor* field = descriptor_->FindFieldByName(name);
if (field == NULL) return NULL;
for (int i = 0; i < unknown_fields_->field_count(); i++) {
if (unknown_fields_->field(i).number() == field->number()) {
return &unknown_fields_->field(i);
}
}
return NULL;
}
// Constructs a protocol buffer which contains fields with all the same
// numbers as all_fields_data_ except that each field is some other wire
// type.
string GetBizarroData() {
unittest::TestEmptyMessage bizarro_message;
UnknownFieldSet* bizarro_unknown_fields =
bizarro_message.mutable_unknown_fields();
for (int i = 0; i < unknown_fields_->field_count(); i++) {
const UnknownField& unknown_field = unknown_fields_->field(i);
if (unknown_field.type() == UnknownField::TYPE_VARINT) {
bizarro_unknown_fields->AddFixed32(unknown_field.number(), 1);
} else {
bizarro_unknown_fields->AddVarint(unknown_field.number(), 1);
}
}
string data;
EXPECT_TRUE(bizarro_message.SerializeToString(&data));
return data;
}
const Descriptor* descriptor_;
unittest::TestAllTypes all_fields_;
string all_fields_data_;
// An empty message that has been parsed from all_fields_data_. So, it has
// unknown fields of every type.
unittest::TestEmptyMessage empty_message_;
UnknownFieldSet* unknown_fields_;
};
TEST_F(UnknownFieldSetTest, AllFieldsPresent) {
// All fields of TestAllTypes should be present, in numeric order (because
// that's the order we parsed them in). Fields that are not valid field
// numbers of TestAllTypes should NOT be present.
int pos = 0;
for (int i = 0; i < 1000; i++) {
const FieldDescriptor* field = descriptor_->FindFieldByNumber(i);
if (field != NULL) {
ASSERT_LT(pos, unknown_fields_->field_count());
EXPECT_EQ(i, unknown_fields_->field(pos++).number());
if (field->is_repeated()) {
// Should have a second instance.
ASSERT_LT(pos, unknown_fields_->field_count());
EXPECT_EQ(i, unknown_fields_->field(pos++).number());
}
}
}
EXPECT_EQ(unknown_fields_->field_count(), pos);
}
TEST_F(UnknownFieldSetTest, Varint) {
const UnknownField* field = GetField("optional_int32");
ASSERT_TRUE(field != NULL);
ASSERT_EQ(UnknownField::TYPE_VARINT, field->type());
EXPECT_EQ(all_fields_.optional_int32(), field->varint());
}
TEST_F(UnknownFieldSetTest, Fixed32) {
const UnknownField* field = GetField("optional_fixed32");
ASSERT_TRUE(field != NULL);
ASSERT_EQ(UnknownField::TYPE_FIXED32, field->type());
EXPECT_EQ(all_fields_.optional_fixed32(), field->fixed32());
}
TEST_F(UnknownFieldSetTest, Fixed64) {
const UnknownField* field = GetField("optional_fixed64");
ASSERT_TRUE(field != NULL);
ASSERT_EQ(UnknownField::TYPE_FIXED64, field->type());
EXPECT_EQ(all_fields_.optional_fixed64(), field->fixed64());
}
TEST_F(UnknownFieldSetTest, LengthDelimited) {
const UnknownField* field = GetField("optional_string");
ASSERT_TRUE(field != NULL);
ASSERT_EQ(UnknownField::TYPE_LENGTH_DELIMITED, field->type());
EXPECT_EQ(all_fields_.optional_string(), field->length_delimited());
}
TEST_F(UnknownFieldSetTest, Group) {
const UnknownField* field = GetField("optionalgroup");
ASSERT_TRUE(field != NULL);
ASSERT_EQ(UnknownField::TYPE_GROUP, field->type());
ASSERT_EQ(1, field->group().field_count());
const UnknownField& nested_field = field->group().field(0);
const FieldDescriptor* nested_field_descriptor =
unittest::TestAllTypes::OptionalGroup::descriptor()->FindFieldByName("a");
ASSERT_TRUE(nested_field_descriptor != NULL);
EXPECT_EQ(nested_field_descriptor->number(), nested_field.number());
ASSERT_EQ(UnknownField::TYPE_VARINT, nested_field.type());
EXPECT_EQ(all_fields_.optionalgroup().a(), nested_field.varint());
}
TEST_F(UnknownFieldSetTest, SerializeFastAndSlowAreEquivalent) {
int size = WireFormat::ComputeUnknownFieldsSize(
empty_message_.unknown_fields());
string slow_buffer;
string fast_buffer;
slow_buffer.resize(size);
fast_buffer.resize(size);
uint8* target = reinterpret_cast<uint8*>(string_as_array(&fast_buffer));
uint8* result = WireFormat::SerializeUnknownFieldsToArray(
empty_message_.unknown_fields(), target);
EXPECT_EQ(size, result - target);
{
io::ArrayOutputStream raw_stream(string_as_array(&slow_buffer), size, 1);
io::CodedOutputStream output_stream(&raw_stream);
WireFormat::SerializeUnknownFields(empty_message_.unknown_fields(),
&output_stream);
ASSERT_FALSE(output_stream.HadError());
}
EXPECT_TRUE(fast_buffer == slow_buffer);
}
TEST_F(UnknownFieldSetTest, Serialize) {
// Check that serializing the UnknownFieldSet produces the original data
// again.
string data;
empty_message_.SerializeToString(&data);
// Don't use EXPECT_EQ because we don't want to dump raw binary data to
// stdout.
EXPECT_TRUE(data == all_fields_data_);
}
TEST_F(UnknownFieldSetTest, ParseViaReflection) {
// Make sure fields are properly parsed to the UnknownFieldSet when parsing
// via reflection.
unittest::TestEmptyMessage message;
io::ArrayInputStream raw_input(all_fields_data_.data(),
all_fields_data_.size());
io::CodedInputStream input(&raw_input);
ASSERT_TRUE(WireFormat::ParseAndMergePartial(&input, &message));
EXPECT_EQ(message.DebugString(), empty_message_.DebugString());
}
TEST_F(UnknownFieldSetTest, SerializeViaReflection) {
// Make sure fields are properly written from the UnknownFieldSet when
// serializing via reflection.
string data;
{
io::StringOutputStream raw_output(&data);
io::CodedOutputStream output(&raw_output);
int size = WireFormat::ByteSize(empty_message_);
WireFormat::SerializeWithCachedSizes(empty_message_, size, &output);
ASSERT_FALSE(output.HadError());
}
// Don't use EXPECT_EQ because we don't want to dump raw binary data to
// stdout.
EXPECT_TRUE(data == all_fields_data_);
}
TEST_F(UnknownFieldSetTest, CopyFrom) {
unittest::TestEmptyMessage message;
message.CopyFrom(empty_message_);
EXPECT_EQ(empty_message_.DebugString(), message.DebugString());
}
TEST_F(UnknownFieldSetTest, Swap) {
unittest::TestEmptyMessage other_message;
ASSERT_TRUE(other_message.ParseFromString(GetBizarroData()));
EXPECT_GT(empty_message_.unknown_fields().field_count(), 0);
EXPECT_GT(other_message.unknown_fields().field_count(), 0);
const string debug_string = empty_message_.DebugString();
const string other_debug_string = other_message.DebugString();
EXPECT_NE(debug_string, other_debug_string);
empty_message_.Swap(&other_message);
EXPECT_EQ(debug_string, other_message.DebugString());
EXPECT_EQ(other_debug_string, empty_message_.DebugString());
}
TEST_F(UnknownFieldSetTest, SwapWithSelf) {
const string debug_string = empty_message_.DebugString();
EXPECT_GT(empty_message_.unknown_fields().field_count(), 0);
empty_message_.Swap(&empty_message_);
EXPECT_GT(empty_message_.unknown_fields().field_count(), 0);
EXPECT_EQ(debug_string, empty_message_.DebugString());
}
TEST_F(UnknownFieldSetTest, MergeFrom) {
unittest::TestEmptyMessage source, destination;
destination.mutable_unknown_fields()->AddVarint(1, 1);
destination.mutable_unknown_fields()->AddVarint(3, 2);
source.mutable_unknown_fields()->AddVarint(2, 3);
source.mutable_unknown_fields()->AddVarint(3, 4);
destination.MergeFrom(source);
EXPECT_EQ(
// Note: The ordering of fields here depends on the ordering of adds
// and merging, above.
"1: 1\n"
"3: 2\n"
"2: 3\n"
"3: 4\n",
destination.DebugString());
}
TEST_F(UnknownFieldSetTest, Clear) {
// Clear the set.
empty_message_.Clear();
EXPECT_EQ(0, unknown_fields_->field_count());
}
TEST_F(UnknownFieldSetTest, ParseKnownAndUnknown) {
// Test mixing known and unknown fields when parsing.
unittest::TestEmptyMessage source;
source.mutable_unknown_fields()->AddVarint(123456, 654321);
string data;
ASSERT_TRUE(source.SerializeToString(&data));
unittest::TestAllTypes destination;
ASSERT_TRUE(destination.ParseFromString(all_fields_data_ + data));
TestUtil::ExpectAllFieldsSet(destination);
ASSERT_EQ(1, destination.unknown_fields().field_count());
ASSERT_EQ(UnknownField::TYPE_VARINT,
destination.unknown_fields().field(0).type());
EXPECT_EQ(654321, destination.unknown_fields().field(0).varint());
}
TEST_F(UnknownFieldSetTest, WrongTypeTreatedAsUnknown) {
// Test that fields of the wrong wire type are treated like unknown fields
// when parsing.
unittest::TestAllTypes all_types_message;
unittest::TestEmptyMessage empty_message;
string bizarro_data = GetBizarroData();
ASSERT_TRUE(all_types_message.ParseFromString(bizarro_data));
ASSERT_TRUE(empty_message.ParseFromString(bizarro_data));
// All fields should have been interpreted as unknown, so the debug strings
// should be the same.
EXPECT_EQ(empty_message.DebugString(), all_types_message.DebugString());
}
TEST_F(UnknownFieldSetTest, WrongTypeTreatedAsUnknownViaReflection) {
// Same as WrongTypeTreatedAsUnknown but via the reflection interface.
unittest::TestAllTypes all_types_message;
unittest::TestEmptyMessage empty_message;
string bizarro_data = GetBizarroData();
io::ArrayInputStream raw_input(bizarro_data.data(), bizarro_data.size());
io::CodedInputStream input(&raw_input);
ASSERT_TRUE(WireFormat::ParseAndMergePartial(&input, &all_types_message));
ASSERT_TRUE(empty_message.ParseFromString(bizarro_data));
EXPECT_EQ(empty_message.DebugString(), all_types_message.DebugString());
}
TEST_F(UnknownFieldSetTest, UnknownExtensions) {
// Make sure fields are properly parsed to the UnknownFieldSet even when
// they are declared as extension numbers.
unittest::TestEmptyMessageWithExtensions message;
ASSERT_TRUE(message.ParseFromString(all_fields_data_));
EXPECT_EQ(message.DebugString(), empty_message_.DebugString());
}
TEST_F(UnknownFieldSetTest, UnknownExtensionsReflection) {
// Same as UnknownExtensions except parsing via reflection.
unittest::TestEmptyMessageWithExtensions message;
io::ArrayInputStream raw_input(all_fields_data_.data(),
all_fields_data_.size());
io::CodedInputStream input(&raw_input);
ASSERT_TRUE(WireFormat::ParseAndMergePartial(&input, &message));
EXPECT_EQ(message.DebugString(), empty_message_.DebugString());
}
TEST_F(UnknownFieldSetTest, WrongExtensionTypeTreatedAsUnknown) {
// Test that fields of the wrong wire type are treated like unknown fields
// when parsing extensions.
unittest::TestAllExtensions all_extensions_message;
unittest::TestEmptyMessage empty_message;
string bizarro_data = GetBizarroData();
ASSERT_TRUE(all_extensions_message.ParseFromString(bizarro_data));
ASSERT_TRUE(empty_message.ParseFromString(bizarro_data));
// All fields should have been interpreted as unknown, so the debug strings
// should be the same.
EXPECT_EQ(empty_message.DebugString(), all_extensions_message.DebugString());
}
TEST_F(UnknownFieldSetTest, UnknownEnumValue) {
using unittest::TestAllTypes;
using unittest::TestAllExtensions;
using unittest::TestEmptyMessage;
const FieldDescriptor* singular_field =
TestAllTypes::descriptor()->FindFieldByName("optional_nested_enum");
const FieldDescriptor* repeated_field =
TestAllTypes::descriptor()->FindFieldByName("repeated_nested_enum");
ASSERT_TRUE(singular_field != NULL);
ASSERT_TRUE(repeated_field != NULL);
string data;
{
TestEmptyMessage empty_message;
UnknownFieldSet* unknown_fields = empty_message.mutable_unknown_fields();
unknown_fields->AddVarint(singular_field->number(), TestAllTypes::BAR);
unknown_fields->AddVarint(singular_field->number(), 5); // not valid
unknown_fields->AddVarint(repeated_field->number(), TestAllTypes::FOO);
unknown_fields->AddVarint(repeated_field->number(), 4); // not valid
unknown_fields->AddVarint(repeated_field->number(), TestAllTypes::BAZ);
unknown_fields->AddVarint(repeated_field->number(), 6); // not valid
empty_message.SerializeToString(&data);
}
{
TestAllTypes message;
ASSERT_TRUE(message.ParseFromString(data));
EXPECT_EQ(TestAllTypes::BAR, message.optional_nested_enum());
ASSERT_EQ(2, message.repeated_nested_enum_size());
EXPECT_EQ(TestAllTypes::FOO, message.repeated_nested_enum(0));
EXPECT_EQ(TestAllTypes::BAZ, message.repeated_nested_enum(1));
const UnknownFieldSet& unknown_fields = message.unknown_fields();
ASSERT_EQ(3, unknown_fields.field_count());
EXPECT_EQ(singular_field->number(), unknown_fields.field(0).number());
ASSERT_EQ(UnknownField::TYPE_VARINT, unknown_fields.field(0).type());
EXPECT_EQ(5, unknown_fields.field(0).varint());
EXPECT_EQ(repeated_field->number(), unknown_fields.field(1).number());
ASSERT_EQ(UnknownField::TYPE_VARINT, unknown_fields.field(1).type());
EXPECT_EQ(4, unknown_fields.field(1).varint());
EXPECT_EQ(repeated_field->number(), unknown_fields.field(2).number());
ASSERT_EQ(UnknownField::TYPE_VARINT, unknown_fields.field(2).type());
EXPECT_EQ(6, unknown_fields.field(2).varint());
}
{
using unittest::optional_nested_enum_extension;
using unittest::repeated_nested_enum_extension;
TestAllExtensions message;
ASSERT_TRUE(message.ParseFromString(data));
EXPECT_EQ(TestAllTypes::BAR,
message.GetExtension(optional_nested_enum_extension));
ASSERT_EQ(2, message.ExtensionSize(repeated_nested_enum_extension));
EXPECT_EQ(TestAllTypes::FOO,
message.GetExtension(repeated_nested_enum_extension, 0));
EXPECT_EQ(TestAllTypes::BAZ,
message.GetExtension(repeated_nested_enum_extension, 1));
const UnknownFieldSet& unknown_fields = message.unknown_fields();
ASSERT_EQ(3, unknown_fields.field_count());
EXPECT_EQ(singular_field->number(), unknown_fields.field(0).number());
ASSERT_EQ(UnknownField::TYPE_VARINT, unknown_fields.field(0).type());
EXPECT_EQ(5, unknown_fields.field(0).varint());
EXPECT_EQ(repeated_field->number(), unknown_fields.field(1).number());
ASSERT_EQ(UnknownField::TYPE_VARINT, unknown_fields.field(1).type());
EXPECT_EQ(4, unknown_fields.field(1).varint());
EXPECT_EQ(repeated_field->number(), unknown_fields.field(2).number());
ASSERT_EQ(UnknownField::TYPE_VARINT, unknown_fields.field(2).type());
EXPECT_EQ(6, unknown_fields.field(2).varint());
}
}
TEST_F(UnknownFieldSetTest, SpaceUsed) {
unittest::TestEmptyMessage empty_message;
// Make sure an unknown field set has zero space used until a field is
// actually added.
int base_size = empty_message.SpaceUsed();
UnknownFieldSet* unknown_fields = empty_message.mutable_unknown_fields();
EXPECT_EQ(base_size, empty_message.SpaceUsed());
// Make sure each thing we add to the set increases the SpaceUsed().
unknown_fields->AddVarint(1, 0);
EXPECT_LT(base_size, empty_message.SpaceUsed());
base_size = empty_message.SpaceUsed();
string* str = unknown_fields->AddLengthDelimited(1);
EXPECT_LT(base_size, empty_message.SpaceUsed());
base_size = empty_message.SpaceUsed();
str->assign(sizeof(string) + 1, 'x');
EXPECT_LT(base_size, empty_message.SpaceUsed());
base_size = empty_message.SpaceUsed();
UnknownFieldSet* group = unknown_fields->AddGroup(1);
EXPECT_LT(base_size, empty_message.SpaceUsed());
base_size = empty_message.SpaceUsed();
group->AddVarint(1, 0);
EXPECT_LT(base_size, empty_message.SpaceUsed());
}
TEST_F(UnknownFieldSetTest, Empty) {
UnknownFieldSet unknown_fields;
EXPECT_TRUE(unknown_fields.empty());
unknown_fields.AddVarint(6, 123);
EXPECT_FALSE(unknown_fields.empty());
unknown_fields.Clear();
EXPECT_TRUE(unknown_fields.empty());
}
} // namespace
} // namespace protobuf
} // namespace google

Some files were not shown because too many files have changed in this diff Show more