Cleaned up indentation & comments

This commit is contained in:
Ben Merritt 2017-02-18 17:11:41 +00:00
parent dcb98bede6
commit 8b94ed59e6
7 changed files with 426 additions and 417 deletions

View File

@ -6,7 +6,7 @@
#include "phonenumbers/phonenumberutil.h" #include "phonenumbers/phonenumberutil.h"
extern "C" { extern "C" {
#include "postgres.h" #include "postgres.h"
} }
#include "short_phone_number.h" #include "short_phone_number.h"
@ -14,24 +14,24 @@ extern "C" {
using namespace i18n::phonenumbers; using namespace i18n::phonenumbers;
static const char* parseErrorMessage(PhoneNumberUtil::ErrorType error) { static const char* parseErrorMessage(PhoneNumberUtil::ErrorType error) {
using PNU = i18n::phonenumbers::PhoneNumberUtil; using PNU = i18n::phonenumbers::PhoneNumberUtil;
switch(error) { switch(error) {
case PNU::NO_PARSING_ERROR: case PNU::NO_PARSING_ERROR:
return "Parsed successfully"; return "Parsed successfully";
case PNU::INVALID_COUNTRY_CODE_ERROR: case PNU::INVALID_COUNTRY_CODE_ERROR:
return "Invalid country code"; return "Invalid country code";
case PNU::NOT_A_NUMBER: case PNU::NOT_A_NUMBER:
return "String does not appear to contain a phone number"; return "String does not appear to contain a phone number";
case PNU::TOO_SHORT_AFTER_IDD: case PNU::TOO_SHORT_AFTER_IDD:
return "Too short after IDD"; return "Too short after IDD";
case PNU::TOO_SHORT_NSN: case PNU::TOO_SHORT_NSN:
return "National number is too short"; return "National number is too short";
case PNU::TOO_LONG_NSN: case PNU::TOO_LONG_NSN:
return "National number is too long"; return "National number is too long";
default: default:
//We have some generic parsing error. //We have some generic parsing error.
return "Unable to parse number"; return "Unable to parse number";
} }
} }
/* /*
@ -39,9 +39,9 @@ static const char* parseErrorMessage(PhoneNumberUtil::ErrorType error) {
*/ */
void reportOutOfMemory() { void reportOutOfMemory() {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY), (errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("Out of memory"))); errmsg("Out of memory")));
} }
/* /*
@ -51,42 +51,42 @@ void reportOutOfMemory() {
* depending on the type of the exception * depending on the type of the exception
*/ */
void reportException(const std::exception& exception) { void reportException(const std::exception& exception) {
{ {
const std::bad_alloc* bad_alloc = dynamic_cast<const std::bad_alloc*>(&exception); const std::bad_alloc* bad_alloc = dynamic_cast<const std::bad_alloc*>(&exception);
if(bad_alloc != nullptr) { if(bad_alloc != nullptr) {
reportOutOfMemory(); reportOutOfMemory();
return; return;
} }
const PhoneNumberTooLongException* too_long = const PhoneNumberTooLongException* too_long =
dynamic_cast<const PhoneNumberTooLongException*>(&exception); dynamic_cast<const PhoneNumberTooLongException*>(&exception);
if(too_long != nullptr) { if(too_long != nullptr) {
std::string phone_number = too_long->number_string(); std::string phone_number = too_long->number_string();
phone_number += '\0'; phone_number += '\0';
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("phone number '%s' is too long", phone_number.data()), errmsg("phone number '%s' is too long", phone_number.data()),
errdetail("%s", exception.what()))); errdetail("%s", exception.what())));
return; return;
} }
} }
//If we don't have a special way to handle this exception, report //If we don't have a special way to handle this exception, report
//a generic error. //a generic error.
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("C++ exception: %s", typeid(exception).name()), errmsg("C++ exception: %s", typeid(exception).name()),
errdetail("%s", exception.what()))); errdetail("%s", exception.what())));
} }
void reportParseError(const char* phone_number, PhoneNumberUtil::ErrorType err) { void reportParseError(const char* phone_number, PhoneNumberUtil::ErrorType err) {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("unable to parse '%s' as a phone number", phone_number), errmsg("unable to parse '%s' as a phone number", phone_number),
errdetail("%s", parseErrorMessage(err)))); errdetail("%s", parseErrorMessage(err))));
} }
void logInfo(const char* msg) { void logInfo(const char* msg) {
ereport(INFO, ereport(INFO,
(errcode(ERRCODE_SUCCESSFUL_COMPLETION), (errcode(ERRCODE_SUCCESSFUL_COMPLETION),
errmsg("%s", msg))); errmsg("%s", msg)));
} }

6
mask.h
View File

@ -1,14 +1,14 @@
#include <algorithm> #include <algorithm>
template<typename T> constexpr T mask(size_t bits, size_t offset = 0) { template<typename T> constexpr T mask(size_t bits, size_t offset = 0) {
return (((T)1 << bits) - 1) << offset; return (((T)1 << bits) - 1) << offset;
} }
template<typename T> constexpr T getMasked(T data, size_t bits, size_t offset) { template<typename T> constexpr T getMasked(T data, size_t bits, size_t offset) {
return (data >> offset) & mask<T>(bits); return (data >> offset) & mask<T>(bits);
} }
//TODO: support typeof(data) != typeof(value)? //TODO: support typeof(data) != typeof(value)?
template<typename T> constexpr T setMasked(T data, T value, size_t bits, size_t offset) { template<typename T> constexpr T setMasked(T data, T value, size_t bits, size_t offset) {
return (data & ~mask<T>(bits, offset)) | ((value & mask<T>(bits)) << offset); return (data & ~mask<T>(bits, offset)) | ((value & mask<T>(bits)) << offset);
} }

View File

@ -4,9 +4,9 @@
#include "phonenumbers/phonenumberutil.h" #include "phonenumbers/phonenumberutil.h"
extern "C" { extern "C" {
#include "postgres.h" #include "postgres.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "fmgr.h" #include "fmgr.h"
} }
#include "error_handling.h" #include "error_handling.h"
@ -16,6 +16,9 @@ using namespace i18n::phonenumbers;
static const PhoneNumberUtil* const phoneUtil = PhoneNumberUtil::GetInstance(); static const PhoneNumberUtil* const phoneUtil = PhoneNumberUtil::GetInstance();
/**
* Clips a value to the given (inclusive) range
*/
template <typename T> template <typename T>
T clip(const T& n, const T& lower, const T& upper) { T clip(const T& n, const T& lower, const T& upper) {
return std::max(lower, std::min(n, upper)); return std::max(lower, std::min(n, upper));
@ -25,36 +28,39 @@ T clip(const T& n, const T& lower, const T& upper) {
* Utility functions * Utility functions
*/ */
/**
* Converts a text object to a C-style string
*/
static char* textToCString(const text* text) { static char* textToCString(const text* text) {
size_t len = VARSIZE(text) - VARHDRSZ; size_t len = VARSIZE(text) - VARHDRSZ;
char* str = (char*)palloc(len + 1); char* str = (char*)palloc(len + 1);
memcpy(str, VARDATA(text), len); memcpy(str, VARDATA(text), len);
str[len] = '\0'; str[len] = '\0';
return str; return str;
} }
//Internal function used by phone_number_in and parse_phone_number //Internal function used by phone_number_in and parse_phone_number
//TODO: take a std::string to minimize copying? //TODO: take a std::string to minimize copying?
ShortPhoneNumber* parsePhoneNumber(const char* number_str, const char* country) { ShortPhoneNumber* parsePhoneNumber(const char* number_str, const char* country) {
PhoneNumber number; PhoneNumber number;
ShortPhoneNumber* short_number; ShortPhoneNumber* short_number;
short_number = (ShortPhoneNumber*)palloc0(sizeof(ShortPhoneNumber)); short_number = (ShortPhoneNumber*)palloc0(sizeof(ShortPhoneNumber));
if(short_number == nullptr) { if(short_number == nullptr) {
throw std::bad_alloc(); throw std::bad_alloc();
} }
PhoneNumberUtil::ErrorType error; PhoneNumberUtil::ErrorType error;
error = phoneUtil->Parse(number_str, country, &number); error = phoneUtil->Parse(number_str, country, &number);
if(error == PhoneNumberUtil::NO_PARSING_ERROR) { if(error == PhoneNumberUtil::NO_PARSING_ERROR) {
//Initialize short_number using placement new. //Initialize short_number using placement new.
new(short_number) ShortPhoneNumber(number); new(short_number) ShortPhoneNumber(number);
return short_number; return short_number;
} else { } else {
reportParseError(number_str, error); reportParseError(number_str, error);
return nullptr; return nullptr;
} }
//TODO: check number validity. //TODO: check number validity.
} }
//TODO: check null args (PG_ARGISNULL) and make non-strict? //TODO: check null args (PG_ARGISNULL) and make non-strict?
@ -64,232 +70,232 @@ ShortPhoneNumber* parsePhoneNumber(const char* number_str, const char* country)
*/ */
extern "C" { extern "C" {
#ifdef PG_MODULE_MAGIC #ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC; PG_MODULE_MAGIC;
#endif #endif
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_in); PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_in);
PGDLLEXPORT Datum PGDLLEXPORT Datum
phone_number_in(PG_FUNCTION_ARGS) { phone_number_in(PG_FUNCTION_ARGS) {
try { try {
const char *number_str = PG_GETARG_CSTRING(0); const char *number_str = PG_GETARG_CSTRING(0);
//TODO: use international format instead. //TODO: use international format instead.
ShortPhoneNumber* number = parsePhoneNumber(number_str, "US"); ShortPhoneNumber* number = parsePhoneNumber(number_str, "US");
if(number) { if(number) {
PG_RETURN_POINTER(number); PG_RETURN_POINTER(number);
} else { } else {
PG_RETURN_NULL(); PG_RETURN_NULL();
} }
} catch(std::exception& e) { } catch(std::exception& e) {
reportException(e); reportException(e);
} }
} }
PGDLLEXPORT PG_FUNCTION_INFO_V1(parse_phone_number);
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(parse_phone_number);
parse_phone_number(PG_FUNCTION_ARGS) {
try {
const text* number_text = PG_GETARG_TEXT_P(0);
const text* country_text = PG_GETARG_TEXT_P(1);
char* number_str = textToCString(number_text); PGDLLEXPORT Datum
char* country = textToCString(country_text); parse_phone_number(PG_FUNCTION_ARGS) {
try {
const text* number_text = PG_GETARG_TEXT_P(0);
const text* country_text = PG_GETARG_TEXT_P(1);
ShortPhoneNumber* number = parsePhoneNumber(number_str, country); char* number_str = textToCString(number_text);
//TODO: prevent leaks. char* country = textToCString(country_text);
pfree(number_str);
pfree(country);
if(number) {
PG_RETURN_POINTER(number);
} else {
PG_RETURN_NULL();
}
} catch(std::exception& e) {
reportException(e);
}
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_out); ShortPhoneNumber* number = parsePhoneNumber(number_str, country);
//TODO: prevent leaks.
pfree(number_str);
pfree(country);
if(number) {
PG_RETURN_POINTER(number);
} else {
PG_RETURN_NULL();
}
} catch(std::exception& e) {
reportException(e);
}
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_out);
phone_number_out(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* short_number = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
PhoneNumber number = *short_number;
std::string formatted; PGDLLEXPORT Datum
phoneUtil->Format(number, PhoneNumberUtil::INTERNATIONAL, &formatted); phone_number_out(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* short_number = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
PhoneNumber number = *short_number;
//Copy the formatted number to a C-style string. std::string formatted;
//We must use the PostgreSQL allocator, not new/malloc. phoneUtil->Format(number, PhoneNumberUtil::INTERNATIONAL, &formatted);
size_t len = formatted.length();
char* result = (char*)palloc(len + 1);
if(result == nullptr) {
throw std::bad_alloc();
}
memcpy(result, formatted.data(), len);
result[len] = '\0';
PG_RETURN_CSTRING(result); //Copy the formatted number to a C-style string.
} catch (const std::exception& e) { //We must use the PostgreSQL allocator, not new/malloc.
reportException(e); size_t len = formatted.length();
} char* result = (char*)palloc(len + 1);
if(result == nullptr) {
throw std::bad_alloc();
}
memcpy(result, formatted.data(), len);
result[len] = '\0';
PG_RETURN_NULL(); PG_RETURN_CSTRING(result);
} } catch (const std::exception& e) {
reportException(e);
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_recv); PG_RETURN_NULL();
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_recv);
phone_number_recv(PG_FUNCTION_ARGS) {
try {
StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
ShortPhoneNumber* number;
number = (ShortPhoneNumber*)palloc(sizeof(ShortPhoneNumber)); PGDLLEXPORT Datum
//TODO: make portable (fix endianness issues, etc.). phone_number_recv(PG_FUNCTION_ARGS) {
pq_copymsgbytes(buf, (char*)number, sizeof(ShortPhoneNumber)); try {
PG_RETURN_POINTER(number); StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
} catch (const std::exception& e) { ShortPhoneNumber* number;
reportException(e);
}
PG_RETURN_NULL(); number = (ShortPhoneNumber*)palloc(sizeof(ShortPhoneNumber));
} //TODO: make portable (fix endianness issues, etc.).
pq_copymsgbytes(buf, (char*)number, sizeof(ShortPhoneNumber));
PG_RETURN_POINTER(number);
} catch (const std::exception& e) {
reportException(e);
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_send); PG_RETURN_NULL();
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_send);
phone_number_send(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber *number = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
StringInfoData buf;
pq_begintypsend(&buf); PGDLLEXPORT Datum
pq_sendbytes(&buf, (const char*)number, sizeof(ShortPhoneNumber)); phone_number_send(PG_FUNCTION_ARGS) {
PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); try {
} catch (const std::exception& e) { const ShortPhoneNumber *number = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
reportException(e); StringInfoData buf;
}
PG_RETURN_NULL(); pq_begintypsend(&buf);
} pq_sendbytes(&buf, (const char*)number, sizeof(ShortPhoneNumber));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
} catch (const std::exception& e) {
reportException(e);
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_equal); PG_RETURN_NULL();
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_equal);
phone_number_equal(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_BOOL(*number1 == *number2); PGDLLEXPORT Datum
} catch(std::exception& e) { phone_number_equal(PG_FUNCTION_ARGS) {
reportException(e); try {
} const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_NULL(); PG_RETURN_BOOL(*number1 == *number2);
} } catch(std::exception& e) {
reportException(e);
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_not_equal); PG_RETURN_NULL();
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_not_equal);
phone_number_not_equal(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_BOOL(*number1 != *number2); PGDLLEXPORT Datum
} catch(std::exception& e) { phone_number_not_equal(PG_FUNCTION_ARGS) {
reportException(e); try {
} const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_NULL(); PG_RETURN_BOOL(*number1 != *number2);
} } catch(std::exception& e) {
reportException(e);
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_less); PG_RETURN_NULL();
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_less);
phone_number_less(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_BOOL(number1->compare_fast(*number2) < 0); PGDLLEXPORT Datum
} catch(std::exception& e) { phone_number_less(PG_FUNCTION_ARGS) {
reportException(e); try {
} const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_NULL(); PG_RETURN_BOOL(number1->compare_fast(*number2) < 0);
} } catch(std::exception& e) {
reportException(e);
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_less_or_equal); PG_RETURN_NULL();
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_less_or_equal);
phone_number_less_or_equal(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_BOOL(number1->compare_fast(*number2) <= 0); PGDLLEXPORT Datum
} catch(std::exception& e) { phone_number_less_or_equal(PG_FUNCTION_ARGS) {
reportException(e); try {
} const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_NULL(); PG_RETURN_BOOL(number1->compare_fast(*number2) <= 0);
} } catch(std::exception& e) {
reportException(e);
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_greater); PG_RETURN_NULL();
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_greater);
phone_number_greater(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_BOOL(number1->compare_fast(*number2) > 0); PGDLLEXPORT Datum
} catch(std::exception& e) { phone_number_greater(PG_FUNCTION_ARGS) {
reportException(e); try {
} const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_NULL(); PG_RETURN_BOOL(number1->compare_fast(*number2) > 0);
} } catch(std::exception& e) {
reportException(e);
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_greater_or_equal); PG_RETURN_NULL();
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_greater_or_equal);
phone_number_greater_or_equal(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_BOOL(number1->compare_fast(*number2) >= 0); PGDLLEXPORT Datum
} catch(std::exception& e) { phone_number_greater_or_equal(PG_FUNCTION_ARGS) {
reportException(e); try {
} const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_NULL(); PG_RETURN_BOOL(number1->compare_fast(*number2) >= 0);
} } catch(std::exception& e) {
reportException(e);
}
PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_cmp); PG_RETURN_NULL();
}
PGDLLEXPORT Datum PGDLLEXPORT PG_FUNCTION_INFO_V1(phone_number_cmp);
phone_number_cmp(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
int64 compared = number1->compare_fast(*number2); PGDLLEXPORT Datum
phone_number_cmp(PG_FUNCTION_ARGS) {
try {
const ShortPhoneNumber* number1 = (ShortPhoneNumber*)PG_GETARG_POINTER(0);
const ShortPhoneNumber* number2 = (ShortPhoneNumber*)PG_GETARG_POINTER(1);
PG_RETURN_INT32(clip<int64>(compared, -1, 1)); int64 compared = number1->compare_fast(*number2);
} catch(std::exception& e) {
reportException(e);
}
PG_RETURN_NULL(); PG_RETURN_INT32(clip<int64>(compared, -1, 1));
} } catch(std::exception& e) {
reportException(e);
}
PG_RETURN_NULL();
}
} }

View File

@ -34,7 +34,7 @@ CREATE TYPE phone_number (
--Cast definitions --Cast definitions
CREATE CAST (phone_number AS text) CREATE CAST (phone_number AS text)
WITH INOUT; WITH INOUT;
--Operator definitions --Operator definitions
@ -42,16 +42,18 @@ CREATE FUNCTION phone_number_equal(phone_number, phone_number) RETURNS bool
LANGUAGE c IMMUTABLE STRICT LANGUAGE c IMMUTABLE STRICT
AS 'pg_libphonenumber', 'phone_number_equal'; AS 'pg_libphonenumber', 'phone_number_equal';
-- TODO: make these operators strict.
CREATE OPERATOR = ( CREATE OPERATOR = (
leftarg = phone_number, leftarg = phone_number,
rightarg = phone_number, rightarg = phone_number,
procedure = phone_number_equal, procedure = phone_number_equal,
commutator = =, commutator = =,
negator = <>, negator = <>,
restrict = eqsel, restrict = eqsel,
join = eqjoinsel, join = eqjoinsel,
hashes = true, hashes = true,
merges = true merges = true
); );
CREATE FUNCTION phone_number_not_equal(phone_number, phone_number) RETURNS bool CREATE FUNCTION phone_number_not_equal(phone_number, phone_number) RETURNS bool
@ -59,13 +61,13 @@ CREATE FUNCTION phone_number_not_equal(phone_number, phone_number) RETURNS bool
AS 'pg_libphonenumber', 'phone_number_not_equal'; AS 'pg_libphonenumber', 'phone_number_not_equal';
CREATE OPERATOR <> ( CREATE OPERATOR <> (
leftarg = phone_number, leftarg = phone_number,
rightarg = phone_number, rightarg = phone_number,
procedure = phone_number_not_equal, procedure = phone_number_not_equal,
commutator = <>, commutator = <>,
negator = =, negator = =,
restrict = neqsel, restrict = neqsel,
join = neqjoinsel join = neqjoinsel
); );
CREATE FUNCTION phone_number_less(phone_number, phone_number) RETURNS bool CREATE FUNCTION phone_number_less(phone_number, phone_number) RETURNS bool
@ -73,13 +75,13 @@ CREATE FUNCTION phone_number_less(phone_number, phone_number) RETURNS bool
AS 'pg_libphonenumber', 'phone_number_less'; AS 'pg_libphonenumber', 'phone_number_less';
CREATE OPERATOR < ( CREATE OPERATOR < (
leftarg = phone_number, leftarg = phone_number,
rightarg = phone_number, rightarg = phone_number,
procedure = phone_number_less, procedure = phone_number_less,
commutator = >, commutator = >,
negator = >=, negator = >=,
restrict = scalarltsel, restrict = scalarltsel,
join = scalarltjoinsel join = scalarltjoinsel
); );
CREATE FUNCTION phone_number_less_or_equal(phone_number, phone_number) RETURNS bool CREATE FUNCTION phone_number_less_or_equal(phone_number, phone_number) RETURNS bool
@ -87,13 +89,13 @@ CREATE FUNCTION phone_number_less_or_equal(phone_number, phone_number) RETURNS b
AS 'pg_libphonenumber', 'phone_number_less_or_equal'; AS 'pg_libphonenumber', 'phone_number_less_or_equal';
CREATE OPERATOR <= ( CREATE OPERATOR <= (
leftarg = phone_number, leftarg = phone_number,
rightarg = phone_number, rightarg = phone_number,
procedure = phone_number_less_or_equal, procedure = phone_number_less_or_equal,
commutator = >=, commutator = >=,
negator = >, negator = >,
restrict = scalarltsel, restrict = scalarltsel,
join = scalarltjoinsel join = scalarltjoinsel
); );
CREATE FUNCTION phone_number_greater(phone_number, phone_number) RETURNS bool CREATE FUNCTION phone_number_greater(phone_number, phone_number) RETURNS bool
@ -101,13 +103,13 @@ CREATE FUNCTION phone_number_greater(phone_number, phone_number) RETURNS bool
AS 'pg_libphonenumber', 'phone_number_greater'; AS 'pg_libphonenumber', 'phone_number_greater';
CREATE OPERATOR > ( CREATE OPERATOR > (
leftarg = phone_number, leftarg = phone_number,
rightarg = phone_number, rightarg = phone_number,
procedure = phone_number_greater, procedure = phone_number_greater,
commutator = >, commutator = >,
negator = <=, negator = <=,
restrict = scalargtsel, restrict = scalargtsel,
join = scalargtjoinsel join = scalargtjoinsel
); );
CREATE FUNCTION phone_number_greater_or_equal(phone_number, phone_number) RETURNS bool CREATE FUNCTION phone_number_greater_or_equal(phone_number, phone_number) RETURNS bool
@ -115,13 +117,13 @@ CREATE FUNCTION phone_number_greater_or_equal(phone_number, phone_number) RETURN
AS 'pg_libphonenumber', 'phone_number_greater_or_equal'; AS 'pg_libphonenumber', 'phone_number_greater_or_equal';
CREATE OPERATOR >= ( CREATE OPERATOR >= (
leftarg = phone_number, leftarg = phone_number,
rightarg = phone_number, rightarg = phone_number,
procedure = phone_number_greater_or_equal, procedure = phone_number_greater_or_equal,
commutator = >=, commutator = >=,
negator = <, negator = <,
restrict = scalargtsel, restrict = scalargtsel,
join = scalargtjoinsel join = scalargtjoinsel
); );
CREATE FUNCTION phone_number_cmp(phone_number, phone_number) RETURNS integer CREATE FUNCTION phone_number_cmp(phone_number, phone_number) RETURNS integer
@ -129,13 +131,13 @@ CREATE FUNCTION phone_number_cmp(phone_number, phone_number) RETURNS integer
AS 'pg_libphonenumber', 'phone_number_cmp'; AS 'pg_libphonenumber', 'phone_number_cmp';
CREATE OPERATOR CLASS phone_number_ops CREATE OPERATOR CLASS phone_number_ops
DEFAULT FOR TYPE phone_number USING btree AS DEFAULT FOR TYPE phone_number USING btree AS
OPERATOR 1 <, OPERATOR 1 <,
OPERATOR 2 <=, OPERATOR 2 <=,
OPERATOR 3 =, OPERATOR 3 =,
OPERATOR 4 >=, OPERATOR 4 >=,
OPERATOR 5 >, OPERATOR 5 >,
FUNCTION 1 phone_number_cmp(phone_number, phone_number); FUNCTION 1 phone_number_cmp(phone_number, phone_number);
--General functions --General functions

View File

@ -6,45 +6,44 @@ using namespace i18n::phonenumbers;
const PhoneNumberUtil* const PhoneNumberTooLongException::phoneUtil = PhoneNumberUtil::GetInstance(); const PhoneNumberUtil* const PhoneNumberTooLongException::phoneUtil = PhoneNumberUtil::GetInstance();
PhoneNumberTooLongException::PhoneNumberTooLongException(const PhoneNumber& number, const char* msg) : PhoneNumberTooLongException::PhoneNumberTooLongException(const PhoneNumber& number, const char* msg) :
_number(number), std::runtime_error(msg) {}; _number(number), std::runtime_error(msg) {};
std::string PhoneNumberTooLongException::number_string() const { std::string PhoneNumberTooLongException::number_string() const {
std::string formatted; std::string formatted;
phoneUtil->Format(number(), PhoneNumberUtil::INTERNATIONAL, &formatted); phoneUtil->Format(number(), PhoneNumberUtil::INTERNATIONAL, &formatted);
return formatted; return formatted;
} }
ShortPhoneNumber::ShortPhoneNumber(i18n::phonenumbers::PhoneNumber number) { ShortPhoneNumber::ShortPhoneNumber(i18n::phonenumbers::PhoneNumber number) {
uint32 country_code = number.country_code(); uint32 country_code = number.country_code();
if(country_code > MAX_COUNTRY_CODE) { if(country_code > MAX_COUNTRY_CODE) {
throw PhoneNumberTooLongException(number, "Country code is too long"); throw PhoneNumberTooLongException(number, "Country code is too long");
} }
this->country_code(country_code); this->country_code(country_code);
uint64 national_number = number.national_number(); uint64 national_number = number.national_number();
if(national_number > MAX_NATIONAL_NUMBER) { if(national_number > MAX_NATIONAL_NUMBER) {
throw PhoneNumberTooLongException(number, "National number is too long"); throw PhoneNumberTooLongException(number, "National number is too long");
} }
this->national_number(national_number); this->national_number(national_number);
if(number.has_number_of_leading_zeros()) { if(number.has_number_of_leading_zeros()) {
uint32 leading_zeros = number.number_of_leading_zeros(); uint32 leading_zeros = number.number_of_leading_zeros();
if(leading_zeros > MAX_LEADING_ZEROS) { if(leading_zeros > MAX_LEADING_ZEROS) {
throw PhoneNumberTooLongException(number, "Too many leading zeros"); throw PhoneNumberTooLongException(number, "Too many leading zeros");
} }
this->leading_zeros(leading_zeros); this->leading_zeros(leading_zeros);
} else { } else {
this->leading_zeros(0); this->leading_zeros(0);
} }
} }
ShortPhoneNumber::operator PhoneNumber() const { ShortPhoneNumber::operator PhoneNumber() const {
PhoneNumber number; PhoneNumber number;
number.set_country_code(country_code()); number.set_country_code(country_code());
number.set_national_number(national_number()); number.set_national_number(national_number());
int32 leading_zeros = this->leading_zeros(); int32 leading_zeros = this->leading_zeros();
number.set_italian_leading_zero(leading_zeros > 0); number.set_italian_leading_zero(leading_zeros > 0);
number.set_number_of_leading_zeros(leading_zeros); number.set_number_of_leading_zeros(leading_zeros);
return number; return number;
} }

View File

@ -6,94 +6,96 @@
#include "mask.h" #include "mask.h"
class PhoneNumberTooLongException : public std::runtime_error { class PhoneNumberTooLongException : public std::runtime_error {
public: public:
PhoneNumberTooLongException(const i18n::phonenumbers::PhoneNumber& number, const char* msg); PhoneNumberTooLongException(const i18n::phonenumbers::PhoneNumber& number, const char* msg);
i18n::phonenumbers::PhoneNumber number() const { i18n::phonenumbers::PhoneNumber number() const {
return _number; return _number;
} }
//TODO: just get the number string from which the PhoneNumber was parsed? (if it exists...) //TODO: just get the number string from which the PhoneNumber was parsed? (if it exists...)
std::string number_string() const; std::string number_string() const;
private: private:
i18n::phonenumbers::PhoneNumber _number; i18n::phonenumbers::PhoneNumber _number;
static const i18n::phonenumbers::PhoneNumberUtil* const phoneUtil; static const i18n::phonenumbers::PhoneNumberUtil* const phoneUtil;
}; };
/**
* Stores a phone number (packed into a 64-bit integer)
*/
class ShortPhoneNumber { class ShortPhoneNumber {
public: public:
enum : size_t { enum : size_t {
MAX_COUNTRY_CODE = 999, MAX_COUNTRY_CODE = 999,
MAX_LEADING_ZEROS = 15, MAX_LEADING_ZEROS = 15,
//15 digits //15 digits
MAX_NATIONAL_NUMBER = 999999999999999, MAX_NATIONAL_NUMBER = 999999999999999,
}; };
enum : size_t {
COUNTRY_CODE_BITS = 10,
LEADING_ZEROS_BITS = 4,
NATIONAL_NUMBER_BITS = 50,
};
enum : size_t {
COUNTRY_CODE_OFFSET = 0,
LEADING_ZEROS_OFFSET = COUNTRY_CODE_OFFSET + COUNTRY_CODE_BITS,
NATIONAL_NUMBER_OFFSET = LEADING_ZEROS_OFFSET + LEADING_ZEROS_BITS,
};
ShortPhoneNumber(i18n::phonenumbers::PhoneNumber number); enum : size_t {
COUNTRY_CODE_BITS = 10,
LEADING_ZEROS_BITS = 4,
NATIONAL_NUMBER_BITS = 50,
};
bool operator == (const ShortPhoneNumber other) const { enum : size_t {
return this->_data == other._data; COUNTRY_CODE_OFFSET = 0,
} LEADING_ZEROS_OFFSET = COUNTRY_CODE_OFFSET + COUNTRY_CODE_BITS,
NATIONAL_NUMBER_OFFSET = LEADING_ZEROS_OFFSET + LEADING_ZEROS_BITS,
bool operator != (const ShortPhoneNumber other) const { };
return !(*this == other);
}
operator i18n::phonenumbers::PhoneNumber() const; ShortPhoneNumber(i18n::phonenumbers::PhoneNumber number);
/* bool operator == (const ShortPhoneNumber other) const {
* Compares to another PhoneNumber using a fast collation heuristic return this->_data == other._data;
* }
* May not produce intuitive results for numbers with the same
* country code but different lengths
*
* Returns:
* - <0 (if a < b)
* - 0 (if a == b)
* - >0 (if a > b)
*/
google::protobuf::int64 compare_fast(ShortPhoneNumber other) const {
return other._data - this->_data;
}
google::protobuf::uint32 country_code() const { bool operator != (const ShortPhoneNumber other) const {
return getMasked(_data, COUNTRY_CODE_BITS, COUNTRY_CODE_OFFSET); return !(*this == other);
} }
void country_code(google::protobuf::uint32 value) { operator i18n::phonenumbers::PhoneNumber() const;
_data = setMasked(_data, (google::protobuf::uint64)value, COUNTRY_CODE_BITS, COUNTRY_CODE_OFFSET);
}
google::protobuf::uint64 national_number() const { /*
return getMasked(_data, NATIONAL_NUMBER_BITS, NATIONAL_NUMBER_OFFSET); * Compares to another PhoneNumber using a fast collation heuristic
} *
* May not produce intuitive results for numbers with the same
* country code but different lengths
*
* Returns:
* - <0 (if a < b)
* - 0 (if a == b)
* - >0 (if a > b)
*/
google::protobuf::int64 compare_fast(ShortPhoneNumber other) const {
return other._data - this->_data;
}
void national_number(google::protobuf::uint64 value) { google::protobuf::uint32 country_code() const {
_data = setMasked(_data, value, NATIONAL_NUMBER_BITS, NATIONAL_NUMBER_OFFSET); return getMasked(_data, COUNTRY_CODE_BITS, COUNTRY_CODE_OFFSET);
} }
google::protobuf::uint64 leading_zeros() const { void country_code(google::protobuf::uint32 value) {
return getMasked(_data, LEADING_ZEROS_BITS, LEADING_ZEROS_OFFSET); _data = setMasked(_data, (google::protobuf::uint64)value, COUNTRY_CODE_BITS, COUNTRY_CODE_OFFSET);
} }
void leading_zeros(google::protobuf::uint64 value) { google::protobuf::uint64 national_number() const {
_data = setMasked(_data, value, LEADING_ZEROS_BITS, LEADING_ZEROS_OFFSET); return getMasked(_data, NATIONAL_NUMBER_BITS, NATIONAL_NUMBER_OFFSET);
} }
private: void national_number(google::protobuf::uint64 value) {
google::protobuf::uint64 _data; _data = setMasked(_data, value, NATIONAL_NUMBER_BITS, NATIONAL_NUMBER_OFFSET);
}
google::protobuf::uint64 leading_zeros() const {
return getMasked(_data, LEADING_ZEROS_BITS, LEADING_ZEROS_OFFSET);
}
void leading_zeros(google::protobuf::uint64 value) {
_data = setMasked(_data, value, LEADING_ZEROS_BITS, LEADING_ZEROS_OFFSET);
}
private:
google::protobuf::uint64 _data;
}; };

View File

@ -3,6 +3,6 @@
#include "../short_phone_number.h" #include "../short_phone_number.h"
int main(int argc, const char** argv) { int main(int argc, const char** argv) {
std::cout << sizeof(ShortPhoneNumber) << std::endl; std::cout << sizeof(ShortPhoneNumber) << std::endl;
return 0; return 0;
} }