blob: 01f34b7d0bac33eb793b18a53695279c7f4890d9 [file] [log] [blame]
// Copyright 2006, Google Inc. All rights reserved.
//
// Paranoid helpers. This is separate from basictypes.h so that it
// can use logging.
#ifndef BASE_PARANOID_H_
#define BASE_PARANOID_H_
#include <glog/logging.h>
#include "kudu/gutil/logging-inl.h"
// Sanitize a bool value which might be sour.
//
// I made up the term "sour bool". It means a bool that is not false (0x0)
// and not true (0x1) but has one of the other 2^N-2 states. A common way
// to create a sour bool is to read an uninitialized bool object.
//
// The Standard says:
// [dcl.init] 8.5 -9- "Otherwise, if no initializer is specified for an
// object, the object and its subobjects, if any, have an indeterminate
// initial value."
// [basic.fundamental] 3.9.1 -5- footnote 42: "Using a bool value in ways
// described by this International standard as "undefined", such as by
// examining the value of an uninitialized automatic variable, might cause
// it to behave as if niether true nor false."
//
// Specifically, this program fragment:
// bool b;
// printf("%d\n", b ? 1 : 0);
// can print any value at all, not just 1 or 0! gcc-4.1.0-piii-linux-opt
// generates code tantamount to "static_cast<int>(b)" with no comparison
// operators. This is harmful for invalid values of b, but fast for all
// valid values.
//
// The original bug was a sour bool that confused the protobuf runtime.
// RawOutputToArray wrote a single byte with the sour bool value and
// ReadBool read a ReadVarint32. If the sour bool did not look like a
// single-byte varint32, then the serialized protobuf would be unreadable.
//
// ===
//
// If you run into a compiler where the volatile pointer does not work, try
// a bit_cast. Just plain "static_cast<unsigned char>(b) ? 1 : 0" does not
// work with gcc-4.1.0-piii-linux-opt, but bit_cast does.
//
// ===
//
// If the assert fires, you probably have an uninitialized bool value. The
// original case of this was an auto struct with an uninitialized bool
// field. It might also be memory corruption, or it might be a new C++
// compiler that has found a new way to hurt us.
//
// ===
//
// Props to Apurv Gupta for the report, Ian Lance Taylor for volatile,
// and Sanjay Ghemawat for general guidance.
//
// -- mec 2006-07-06
inline bool SanitizeBool(bool b) {
unsigned char c = static_cast<unsigned char>(b);
volatile unsigned char* p = &c;
DCHECK_LT(*p, 2);
return (*p != '\0') ? true : false;
}
// Returns true iff. a given bool is either true (0x1) or false (0x0).
// Mainly used for sanity checking for set_field(bool) in Protocol Buffer.
//
// This sanity checking is necessary since a sour bool might confuse the
// Protocol Buffer runtime as mentioned above.
//
// Uses an assembler sequence so as not to be compiler-optimization sensitive.
inline bool IsSaneBool(bool b) {
#if (defined __i386__ || defined __x86_64__) && defined __GNUC__
bool result;
// Set result to true if b is below or equal to 0x1.
__asm__("cmpb $0x1, %1\n\t"
"setbe %0"
: "=m" (result) // Output spec
: "m" (b) // Input spec
: "cc"); // Clobbers condition-codes
return result;
#else
unsigned char c = static_cast<unsigned char>(b);
volatile unsigned char* p = &c;
return *p <= 1;
#endif
}
#endif // BASE_PARANOID_H_