![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 /* currency.cc 00002 Jeremy Barnes, 5 November 2012 00003 Copyright (c) 2012 Datacratic Inc. All rights reserved. 00004 00005 */ 00006 00007 #include "rtbkit/common/currency.h" 00008 00009 using namespace std; 00010 using namespace ML; 00011 using namespace Datacratic; 00012 00013 00014 namespace RTBKIT { 00015 00016 00017 /*****************************************************************************/ 00018 /* CURRENCY CODE */ 00019 /*****************************************************************************/ 00020 00021 std::string toString(CurrencyCode code) 00022 { 00023 if (code == CurrencyCode::CC_NONE) 00024 return "NONE"; 00025 else if (code == CurrencyCode::CC_USD) 00026 return "USD"; 00027 else throw ML::Exception("unknown currency code"); 00028 } 00029 00030 CurrencyCode parseCurrencyCode(const std::string & code) 00031 { 00032 if (code == "USD") 00033 return CurrencyCode::CC_USD; 00034 else if (code == "NONE") 00035 return CurrencyCode::CC_NONE; 00036 else throw ML::Exception("unknown currency code"); 00037 } 00038 00039 CurrencyCode jsonDecode(const Json::Value & j, CurrencyCode *) 00040 { 00041 if (j.isNull()) 00042 return CurrencyCode::CC_NONE; 00043 string s = j.asString(); 00044 if (s.size() > 4) 00045 throw ML::Exception("unknown currency code " + s); 00046 if (s == "USD") 00047 return CurrencyCode::CC_USD; 00048 00049 throw ML::Exception("not done: decoding other currency " + s); 00050 } 00051 00052 Json::Value jsonEncode(CurrencyCode code) 00053 { 00054 switch (code) { 00055 case CurrencyCode::CC_NONE: 00056 return Json::Value(); 00057 case CurrencyCode::CC_USD: 00058 return Json::Value("USD"); 00059 default: 00060 throw ML::Exception("not done: encoding other currencies"); 00061 } 00062 } 00063 00064 /*****************************************************************************/ 00065 /* AMOUNT */ 00066 /*****************************************************************************/ 00067 00068 00069 Json::Value 00070 Amount:: 00071 toJson() const 00072 { 00073 if (isZero()) 00074 return 0; 00075 00076 Json::Value result(Json::arrayValue); 00077 result[0] = value; 00078 result[1] = getCurrencyStr(); 00079 00080 //cerr << "Amount toJson() gave " << result.toString() << endl; 00081 00082 return result; 00083 } 00084 00085 std::string 00086 Amount:: 00087 getCurrencyStr(CurrencyCode currencyCode) 00088 { 00089 switch (currencyCode) { 00090 case CurrencyCode::CC_NONE: return "NONE"; 00091 case CurrencyCode::CC_USD: return "USD/1M"; 00092 default: 00093 throw ML::Exception("unknown currency code %d", (uint32_t)currencyCode); 00094 } 00095 } 00096 00097 std::string 00098 Amount:: 00099 getCurrencyStr() const 00100 { 00101 return getCurrencyStr(currencyCode); 00102 } 00103 00104 std::string 00105 Amount:: 00106 toString() const 00107 { 00108 if (!*this) 00109 return "0"; 00110 return to_string(value) + getCurrencyStr(); 00111 } 00112 00113 Amount 00114 Amount:: 00115 fromJson(const Json::Value & val) 00116 { 00117 //cerr << "Amount fromJson " << val.toString() << endl; 00118 00119 if (val.isArray()) { 00120 if (val.size() != 2) 00121 throw ML::Exception("invalid amount size"); 00122 return Amount(val[1].asString(), val[0].asInt()); 00123 } 00124 else if (val.isString()) 00125 return parse(val.asString()); 00126 else if (val.isNumeric() && val.asDouble() == 0.0) 00127 return Amount(); 00128 else if (val.isNull()) 00129 return Amount(); 00130 else if (val.isObject()) { 00131 string currencyCode = "NONE"; 00132 int64_t value = 0; 00133 for (auto it = val.begin(), end = val.end(); it != end; ++it) { 00134 if (it.memberName() == "value") 00135 value = it->asInt(); 00136 else if (it.memberName() == "currencyCode") 00137 currencyCode = it->asString(); 00138 else throw ML::Exception("unknown Amount field " + it.memberName()); 00139 } 00140 return Amount(currencyCode, value); 00141 } 00142 else throw ML::Exception("unknown amount " + val.toString()); 00143 } 00144 00145 Amount 00146 Amount:: 00147 parse(const std::string & value) 00148 { 00149 if (value == "0") 00150 return Amount(); 00151 00152 const char * p = value.c_str(); 00153 char * ep = 0; 00154 errno = 0; 00155 long long val = strtoll(p, &ep, 10); 00156 if (errno != 0) 00157 throw ML::Exception("invalid amount parsed"); 00158 string currency(ep); 00159 00160 if (currency == "") { 00161 throw ML::Exception("no currency on Amount " + value); 00162 } 00163 00164 return Amount(currency, val); 00165 } 00166 00167 CurrencyCode 00168 Amount:: 00169 parseCurrency(const std::string & currency) 00170 { 00171 if (currency == "NONE") 00172 return CurrencyCode::CC_NONE; 00173 else if (currency == "USD/1M") 00174 return CurrencyCode::CC_USD; 00175 else throw ML::Exception("unknown currency code " + currency); 00176 } 00177 00178 std::ostream & 00179 operator << (std::ostream & stream, Amount amount) 00180 { 00181 return stream << amount.toString(); 00182 } 00183 00184 void 00185 Amount:: 00186 assertCurrencyIsCompatible(const Amount & other) const 00187 { 00188 if (!currencyIsCompatible(other)) 00189 throw ML::Exception("currencies are not compatible: " 00190 + toString() + " vs " + other.toString()); 00191 } 00192 00193 00194 bool 00195 Amount:: 00196 operator < (const Amount & other) const 00197 { 00198 assertCurrencyIsCompatible(other); 00199 return value < other.value; 00200 } 00201 00202 bool 00203 Amount:: 00204 operator <= (const Amount & other) const 00205 { 00206 assertCurrencyIsCompatible(other); 00207 return value <= other.value; 00208 } 00209 00210 bool 00211 Amount:: 00212 operator > (const Amount & other) const 00213 { 00214 assertCurrencyIsCompatible(other); 00215 return value > other.value; 00216 } 00217 00218 bool 00219 Amount:: 00220 operator >= (const Amount & other) const 00221 { 00222 assertCurrencyIsCompatible(other); 00223 return value >= other.value; 00224 } 00225 00226 void 00227 Amount:: 00228 serialize(ML::DB::Store_Writer & store) const 00229 { 00230 if (!*this) { 00231 store << (unsigned char)0; 00232 return; 00233 } 00234 00235 store << (unsigned char)(1 + (value < 0)) 00236 << (uint32_t)currencyCode 00237 << ML::DB::compact_size_t(value >= 0 ? value : -value); // need compact_int_t as it is signed 00238 } 00239 00240 void 00241 Amount:: 00242 reconstitute(ML::DB::Store_Reader & store) 00243 { 00244 unsigned char version; 00245 store >> version; 00246 if (version == 0) { 00247 *this = Amount(); 00248 return; 00249 } 00250 00251 if (version > 2) 00252 throw ML::Exception("invalid version reconstituting Amount"); 00253 00254 uint32_t ccode; 00255 store >> ccode; 00256 currencyCode = (CurrencyCode)ccode; 00257 value = ML::DB::compact_size_t(store); 00258 if (version == 2) 00259 value = -value; 00260 00261 //cerr << "reconstituted = " << *this << endl; 00262 } 00263 00264 00265 /*****************************************************************************/ 00266 /* CURRENCY POOL */ 00267 /*****************************************************************************/ 00268 00269 bool 00270 CurrencyPool:: 00271 operator == (const CurrencyPool & other) const 00272 { 00273 auto checkContains = [] (const CurrencyPool & pool1, 00274 const CurrencyPool & pool2) 00275 { 00276 for (auto amt: pool1.currencyAmounts) { 00277 if (pool2.getAvailable(amt.currencyCode) != amt) 00278 return false; 00279 } 00280 00281 return true; 00282 }; 00283 00284 return checkContains(*this, other) && checkContains(other, *this); 00285 } 00286 00287 Amount 00288 CurrencyPool:: 00289 getAvailable(const CurrencyCode & currency) const 00290 { 00291 for (auto & am: currencyAmounts) { 00292 if (am.currencyCode == currency) { 00293 return am; 00294 } 00295 } 00296 00297 return Amount(currency, 0); 00298 } 00299 00300 bool 00301 CurrencyPool:: 00302 hasAvailable(const Amount & amount) const 00303 { 00304 return getAvailable(amount.currencyCode).value >= amount.value; 00305 } 00306 00307 Json::Value 00308 CurrencyPool:: 00309 toJson() const 00310 { 00311 Json::Value result(Json::objectValue); 00312 for (auto a: currencyAmounts) 00313 result[a.getCurrencyStr()] = a.value; 00314 return result; 00315 } 00316 00317 CurrencyPool 00318 CurrencyPool:: 00319 fromJson(const Json::Value & json) 00320 { 00321 CurrencyPool result; 00322 00323 for (auto it = json.begin(), end = json.end(); it != end; ++it) 00324 result += Amount(it.memberName(), (*it).asInt()); 00325 00326 //cerr << "getting currencyPool from JSON " << json 00327 // << " returned " << result << endl; 00328 00329 return result; 00330 } 00331 00332 std::string 00333 CurrencyPool:: 00334 toString() const 00335 { 00336 if (isZero()) 00337 return "0"; 00338 00339 string result; 00340 for (unsigned i = 0; i < currencyAmounts.size(); ++i) { 00341 if (i > 0) 00342 result += ", "; 00343 result += currencyAmounts[i].toString(); 00344 } 00345 return result; 00346 } 00347 00348 std::ostream & operator << (std::ostream & stream, CurrencyPool pool) 00349 { 00350 return stream << pool.toString(); 00351 } 00352 00353 void 00354 CurrencyPool:: 00355 serialize(ML::DB::Store_Writer & store) const 00356 { 00357 store << (unsigned char)0 00358 << currencyAmounts; 00359 } 00360 00361 void 00362 CurrencyPool:: 00363 reconstitute(ML::DB::Store_Reader & store) 00364 { 00365 unsigned char version; 00366 store >> version; 00367 if (version != 0) 00368 throw ML::Exception("error reconstituting currency pool"); 00369 store >> currencyAmounts; 00370 } 00371 00372 bool 00373 CurrencyPool:: 00374 isSameOrPastVersion(const CurrencyPool & otherPool) 00375 const 00376 { 00377 for (const Amount & amount: currencyAmounts) { 00378 const Amount otherAmount 00379 = otherPool.getAvailable(amount.currencyCode); 00380 if (amount < otherAmount) 00381 return false; 00382 } 00383 00384 return true; 00385 } 00386 00387 /*****************************************************************************/ 00388 /* LINE ITEMS */ 00389 /*****************************************************************************/ 00390 00391 00392 LineItems 00393 LineItems:: 00394 fromJson(const Json::Value & json) 00395 { 00396 LineItems result; 00397 00398 for (auto it = json.begin(), end = json.end(); it != end; ++it) 00399 result[it.memberName()] = CurrencyPool::fromJson(*it); 00400 00401 return result; 00402 } 00403 00404 Json::Value 00405 LineItems:: 00406 toJson() const 00407 { 00408 Json::Value result(Json::objectValue); 00409 for (auto & e: entries) 00410 result[e.first] = e.second.toJson(); 00411 return result; 00412 } 00413 00414 bool 00415 LineItems:: 00416 operator == (const LineItems & other) const 00417 { 00418 auto compareLineItems = [] (const LineItems & li1, 00419 const LineItems & li2) 00420 { 00421 for (auto & e: li1.entries) { 00422 if (e.second != li2[e.first]) 00423 return false; 00424 } 00425 return true; 00426 }; 00427 00428 return compareLineItems(*this, other) && compareLineItems(other, *this); 00429 } 00430 00431 LineItems & 00432 LineItems:: 00433 operator += (const LineItems & other) 00434 { 00435 for (auto & e: other.entries) 00436 (*this)[e.first] += e.second; 00437 return *this; 00438 } 00439 00440 void 00441 LineItems:: 00442 serialize(ML::DB::Store_Writer & store) const 00443 { 00444 store << (unsigned char)0 // version 00445 << entries; 00446 } 00447 00448 void 00449 LineItems:: 00450 reconstitute(ML::DB::Store_Reader & store) 00451 { 00452 unsigned char version; 00453 store >> version; 00454 if (version != 0) 00455 throw ML::Exception("error reconstituting currency pool"); 00456 store >> entries; 00457 } 00458 00459 00460 /*****************************************************************************/ 00461 /* VALUE DESCRIPTIONS */ 00462 /*****************************************************************************/ 00463 00464 struct LineItemsDescription 00465 : public Datacratic::ValueDescriptionT<LineItems> { 00466 00467 LineItemsDescription() 00468 { 00469 } 00470 00471 virtual void parseJsonTyped(LineItems * val, 00472 JsonParsingContext & context) const 00473 { 00474 *val = std::move(LineItems::fromJson(context.expectJson())); 00475 } 00476 00477 virtual void printJsonTyped(const LineItems * val, 00478 JsonPrintingContext & context) const 00479 { 00480 context.writeJson(val->toJson()); 00481 } 00482 00483 virtual bool isDefaultTyped(const LineItems * val) const 00484 { 00485 return val->empty(); 00486 } 00487 }; 00488 00489 struct AmountDescription 00490 : public Datacratic::ValueDescriptionT<Amount> { 00491 00492 AmountDescription() 00493 { 00494 } 00495 00496 virtual void parseJsonTyped(Amount * val, 00497 JsonParsingContext & context) const 00498 { 00499 *val = std::move(Amount::fromJson(context.expectJson())); 00500 //cerr << "parsing amount JSON" << *val << endl; 00501 } 00502 00503 virtual void printJsonTyped(const Amount * val, 00504 JsonPrintingContext & context) const 00505 { 00506 context.writeJson(val->toJson()); 00507 } 00508 00509 virtual bool isDefaultTyped(const Amount * val) const 00510 { 00511 return val->isZero(); 00512 } 00513 }; 00514 00515 struct CurrencyPoolDescription 00516 : public Datacratic::ValueDescriptionT<CurrencyPool> { 00517 00518 CurrencyPoolDescription() 00519 { 00520 } 00521 00522 virtual void parseJsonTyped(CurrencyPool * val, 00523 JsonParsingContext & context) const 00524 { 00525 *val = std::move(CurrencyPool::fromJson(context.expectJson())); 00526 } 00527 00528 virtual void printJsonTyped(const CurrencyPool * val, 00529 JsonPrintingContext & context) const 00530 { 00531 context.writeJson(val->toJson()); 00532 } 00533 00534 virtual bool isDefaultTyped(const CurrencyPool * val) const 00535 { 00536 return val->empty(); 00537 } 00538 }; 00539 00540 struct CurrencyCodeDescription 00541 : public Datacratic::EnumDescription<CurrencyCode> { 00542 00543 CurrencyCodeDescription() 00544 { 00545 } 00546 00547 virtual void parseJsonTyped(CurrencyCode * val, 00548 JsonParsingContext & context) const 00549 { 00550 std::string s = context.expectStringAscii(); 00551 *val = parseCurrencyCode(s); 00552 } 00553 00554 virtual void printJsonTyped(const CurrencyCode * val, 00555 JsonPrintingContext & context) const 00556 { 00557 context.writeString(toString(*val)); 00558 } 00559 00560 virtual bool isDefaultTyped(const CurrencyCode * val) const 00561 { 00562 return false; 00563 } 00564 00565 }; 00566 00567 ValueDescriptionT<LineItems> * getDefaultDescription(LineItems *) 00568 { 00569 return new LineItemsDescription(); 00570 } 00571 00572 ValueDescriptionT<CurrencyPool> * getDefaultDescription(CurrencyPool *) 00573 { 00574 return new CurrencyPoolDescription(); 00575 } 00576 00577 ValueDescriptionT<Amount> * getDefaultDescription(Amount *) 00578 { 00579 return new AmountDescription(); 00580 } 00581 00582 ValueDescriptionT<CurrencyCode> * getDefaultDescription(CurrencyCode *) 00583 { 00584 return new CurrencyCodeDescription(); 00585 } 00586 00587 } // namespace RTBKIT 00588