1 /++ 2 Authros: Ilya Yaroshenko 3 +/ 4 module mir.ion.ser.bloomberg; 5 6 import mir.ion.exception: IonException; 7 public import mir.bloomberg.blpapi : BloombergElement = Element; 8 static import blpapi = mir.bloomberg.blpapi; 9 10 private alias validate = blpapi.validateBloombergErroCode; 11 12 private static immutable bloombergClobSerializationIsntImplemented = new IonException("Bloomberg CLOB serialization isn't implemented."); 13 private static immutable bloombergBlobSerializationIsntImplemented = new IonException("Bloomberg BLOB serialization isn't implemented."); 14 15 /++ 16 Ion serialization back-end 17 +/ 18 struct BloombergSerializer() 19 { 20 import mir.format: stringBuf, getData; 21 import mir.bignum.decimal: Decimal; 22 import mir.bignum.low_level_view: WordEndian; 23 import mir.bignum.integer: BigInt; 24 import mir.ion.type_code; 25 import mir.lob; 26 import mir.timestamp; 27 import mir.serde: SerdeTarget; 28 import std.traits: isNumeric; 29 30 BloombergElement* nextValue; 31 32 BloombergElement* aggregateValue; 33 34 stringBuf currentPartString; 35 36 /// Mutable value used to choose format specidied or user-defined serialization specializations 37 int serdeTarget = SerdeTarget.bloomberg; 38 39 private uint valueIndex; 40 41 @safe pure: 42 43 private const(char)* toScopeStringz(scope const(char)[] value) @trusted return scope nothrow 44 { 45 currentPartString.reset; 46 currentPartString.put(value); 47 currentPartString.put('\0'); 48 return currentPartString.data.ptr; 49 } 50 51 private void pushState(BloombergElement* state) 52 { 53 aggregateValue = state; 54 nextValue = null; 55 } 56 57 private BloombergElement* popState() 58 { 59 auto state = aggregateValue; 60 aggregateValue = nextValue; 61 nextValue = null; 62 return state; 63 } 64 65 BloombergElement* stringBegin() 66 { 67 currentPartString.reset; 68 return null; 69 } 70 71 /++ 72 Puts string part. The implementation allows to split string unicode points. 73 +/ 74 void putStringPart(scope const char[] value) 75 { 76 import mir.format: printEscaped, EscapeFormat; 77 currentPartString.put(value); 78 } 79 80 void stringEnd(BloombergElement*) @trusted 81 { 82 if (currentPartString.length == 1) 83 { 84 blpapi.setValueChar(nextValue, *currentPartString.data.ptr, valueIndex).validate; 85 } 86 else 87 { 88 currentPartString.put('\0'); 89 blpapi.setValueString(nextValue, currentPartString.data.ptr, valueIndex).validate; 90 } 91 } 92 93 private blpapi.Name* getName(scope const char* str) 94 { 95 if (auto name = blpapi.nameFindName(str)) 96 return name; 97 return blpapi.nameCreate(str); 98 } 99 100 private blpapi.Name* getName(scope const char[] str) 101 { 102 return getName(toScopeStringz(str)); 103 } 104 105 void putSymbolPtr(scope const char* value) 106 { 107 auto name = getName(value); 108 blpapi.setValueFromName(nextValue, name, valueIndex).validate; 109 blpapi.nameDestroy(name); 110 } 111 112 void putSymbol(scope const char[] value) 113 { 114 return putSymbolPtr(toScopeStringz(value)); 115 } 116 117 BloombergElement* structBegin(size_t length = 0) 118 { 119 return popState; 120 } 121 122 void structEnd(BloombergElement* state) 123 { 124 pushState(state); 125 } 126 127 BloombergElement* listBegin(size_t length = 0) 128 { 129 valueIndex = uint.max; 130 return null; 131 } 132 133 void listEnd(BloombergElement* state) 134 { 135 valueIndex = 0; 136 nextValue = null; 137 } 138 139 alias sexpBegin = listBegin; 140 141 alias sexpEnd = listEnd; 142 143 BloombergElement* annotationsBegin() 144 { 145 return aggregateValue; 146 } 147 148 void putAnnotationPtr(scope const char* value) 149 { 150 aggregateValue = nextValue; 151 auto name = getName(value); 152 blpapi.setChoice(nextValue, nextValue, null, name, 0).validate; 153 blpapi.nameDestroy(name); 154 } 155 156 void putAnnotation(scope const char[] value) @trusted 157 { 158 putAnnotationPtr(toScopeStringz(value)); 159 } 160 161 void annotationsEnd(BloombergElement* state) 162 { 163 aggregateValue = state; 164 } 165 166 BloombergElement* annotationWrapperBegin() 167 { 168 return null; 169 } 170 171 void annotationWrapperEnd(BloombergElement*) 172 { 173 } 174 175 void nextTopLevelValue() 176 { 177 static immutable exc = new IonException("Can't serialize to multiple Bloomberg Elements at once."); 178 throw exc; 179 } 180 181 void putKeyPtr(scope const char* key) 182 { 183 nextValue = null; 184 auto name = getName(key); 185 blpapi.getElement(aggregateValue, nextValue, null, name).validate; 186 blpapi.nameDestroy(name); 187 assert(nextValue !is null); 188 } 189 190 void putKey(scope const char[] key) 191 { 192 putKeyPtr(toScopeStringz(key)); 193 } 194 195 void putValue(Num)(const Num value) 196 if (isNumeric!Num && !is(Num == enum)) 197 { 198 import mir.internal.utility: isFloatingPoint; 199 200 assert(nextValue); 201 static if (isFloatingPoint!Num) 202 { 203 if (float(value) is value) 204 { 205 blpapi.setValueFloat32(nextValue, value, valueIndex).validate; 206 } 207 else 208 { 209 blpapi.setValueFloat64(nextValue, value, valueIndex).validate; 210 } 211 } 212 else 213 static if (is(Num == int) || Num.sizeof <= 2) 214 { 215 static if (is(Num == ulong)) 216 { 217 if (value > long.max) 218 { 219 static immutable exc = new SerdeException("BloombergSerializer: integer overflow"); 220 throw exc; 221 } 222 } 223 224 (cast(int) value == cast(long) value 225 ? blpapi.setValueInt32(nextValue, value, 0) 226 : blpapi.setValueInt64(nextValue, value, 0)) 227 .validate; 228 } 229 } 230 231 void putValue(W, WordEndian endian)(BigIntView!(W, endian) view) 232 { 233 auto i = cast(long) view; 234 if (view != i) 235 { 236 static immutable exc = new SerdeException("BloombergSerializer: integer overflow"); 237 throw exc; 238 } 239 putValue(num); 240 } 241 242 void putValue(size_t size)(auto ref const BigInt!size num) 243 { 244 putValue(num.view); 245 } 246 247 void putValue(size_t size)(auto ref const Decimal!size num) 248 { 249 putValue(cast(double)num); 250 } 251 252 void putValue(typeof(null)) 253 { 254 assert(nextValue); 255 } 256 257 /// ditto 258 void putNull(IonTypeCode code) 259 { 260 putValue(null); 261 } 262 263 void putValue(bool b) 264 { 265 assert(nextValue); 266 blpapi.setValueBool(nextValue, b, valueIndex).validate; 267 } 268 269 void putValue(scope const char[] value) 270 { 271 auto state = stringBegin; 272 putStringPart(value); 273 stringEnd(state); 274 } 275 276 void putValue(Clob value) 277 { 278 throw bloombergClobSerializationIsntImplemented; 279 } 280 281 void putValue(Blob value) 282 { 283 throw bloombergBlobSerializationIsntImplemented; 284 } 285 286 void putValue(Timestamp value) 287 { 288 blpapi.HighPrecisionDatetime dt = value; 289 blpapi.setValueHighPrecisionDatetime(nextValue, dt, valueIndex).validate; 290 } 291 292 void elemBegin() 293 { 294 } 295 296 alias sexpElemBegin = elemBegin; 297 } 298 299 private static immutable excBytearray = new Exception("Mir Bloomberg: unexpected data type: bytearray"); 300 private static immutable excCorrelationOd = new Exception("Mir Bloomberg: unexpected data type: correlation_id"); 301 302 /// 303 void serializeValue(S)(ref S serializer, const(BloombergElement)* value) 304 { 305 import core.stdc.string: strlen; 306 import mir.ion.type_code; 307 import mir.timestamp: Timestamp; 308 static import blpapi = mir.bloomberg.blpapi; 309 import mir.bloomberg.blpapi: validate = validateBloombergErroCode; 310 311 if (value is null || blpapi.isNull(value)) 312 { 313 serializer.putValue(null); 314 return; 315 } 316 317 auto isArray = blpapi.isArray(value); 318 auto type = blpapi.datatype(value); 319 size_t arrayLength = 1; 320 typeof(serializer.listBegin(arrayLength)) arrayState; 321 if (isArray) 322 { 323 arrayLength = blpapi.numValues(value); 324 arrayState = serializer.listBegin(arrayLength); 325 if (type == blpapi.DataType.choice || type == blpapi.DataType.sequence) 326 { 327 foreach(index; 0 .. arrayLength) 328 { 329 BloombergElement* v; 330 blpapi.getValueAsElement(value, v, index).validate; 331 serializer.elemBegin; 332 serializer.serializeValue(v); 333 } 334 serializer.listEnd(arrayState); 335 return; 336 } 337 } 338 foreach(index; 0 .. arrayLength) 339 { 340 if (isArray) 341 serializer.elemBegin; 342 final switch (type) 343 { 344 case blpapi.DataType.null_: 345 serializer.putValue(null); 346 continue; 347 case blpapi.DataType.bool_: { 348 blpapi.Bool v; 349 blpapi.getValueAsBool(value, v, index).validate; 350 serializer.putValue(cast(bool)v); 351 continue; 352 } 353 case blpapi.DataType.char_: { 354 char[1] v; 355 blpapi.getValueAsChar(value, v[0], index).validate; 356 serializer.putValue(v[]); 357 continue; 358 } 359 case blpapi.DataType.byte_: 360 case blpapi.DataType.int32: { 361 int v; 362 blpapi.getValueAsInt32(value, v, index).validate; 363 serializer.putValue(v); 364 continue; 365 } 366 case blpapi.DataType.int64: { 367 long v; 368 blpapi.getValueAsInt64(value, v, index).validate; 369 serializer.putValue(v); 370 continue; 371 } 372 case blpapi.DataType.float32: { 373 float v; 374 blpapi.getValueAsFloat32(value, v, index).validate; 375 serializer.putValue(v); 376 continue; 377 } 378 case blpapi.DataType.decimal: 379 case blpapi.DataType.float64: { 380 double v; 381 blpapi.getValueAsFloat64(value, v, index).validate; 382 serializer.putValue(v); 383 continue; 384 } 385 case blpapi.DataType..string: { 386 const(char)* v; 387 blpapi.getValueAsString(value, v, index).validate; 388 serializer.putValue(v[0 .. (()@trusted => v.strlen)()]); 389 continue; 390 } 391 case blpapi.DataType.date: 392 case blpapi.DataType.time: 393 case blpapi.DataType.datetime: { 394 blpapi.HighPrecisionDatetime v; 395 blpapi.getValueAsHighPrecisionDatetime(value, v, index).validate; 396 if (v.parts) 397 serializer.putValue(cast(Timestamp)v); 398 else 399 serializer.putNull(IonTypeCode.timestamp); 400 continue; 401 } 402 case blpapi.DataType.enumeration: { 403 const(char)* v; 404 blpapi.getValueAsString(value, v, index).validate; 405 if (v is null) 406 serializer.putNull(IonTypeCode.symbol); 407 else 408 serializer.putSymbol(v[0 .. (()@trusted => v.strlen)()]); 409 continue; 410 } 411 case blpapi.DataType.sequence: { 412 auto length = blpapi.numElements(value); 413 auto state = serializer.structBegin(length); 414 foreach(i; 0 .. length) 415 { 416 BloombergElement* v; 417 blpapi.getElementAt(value, v, i).validate; 418 blpapi.Name* name = blpapi.name(v); 419 const(char)* keyPtr = name ? blpapi.nameString(name) : null; 420 auto key = keyPtr ? keyPtr[0 .. (()@trusted => keyPtr.strlen)()] : null; 421 serializer.putKey(key); 422 serializer.serializeValue(v); 423 } 424 serializer.structEnd(state); 425 continue; 426 } 427 case blpapi.DataType.choice: { 428 auto wrapperState = serializer.annotationWrapperBegin; 429 auto annotationsState = serializer.annotationsBegin; 430 do 431 { 432 BloombergElement* v; 433 blpapi.getChoice(value, v).validate; 434 blpapi.Name* name = blpapi.name(v); 435 const(char)* annotationPtr = name ? blpapi.nameString(name) : null; 436 auto annotation = annotationPtr ? annotationPtr[0 .. (()@trusted => annotationPtr.strlen)()] : null; 437 serializer.putAnnotation(annotation); 438 value = v; 439 } 440 while (blpapi.datatype(value) == blpapi.DataType.choice); 441 serializer.annotationsEnd(annotationsState); 442 serializer.serializeValue(value); 443 serializer.annotationWrapperEnd(wrapperState); 444 continue; 445 } 446 case blpapi.DataType.bytearray: 447 throw excBytearray; 448 case blpapi.DataType.correlation_id: 449 throw excCorrelationOd; 450 } 451 } 452 if (isArray) 453 { 454 serializer.listEnd(arrayState); 455 } 456 } 457 458 version(mir_ion_test) 459 unittest 460 { 461 import mir.ion.ser.bloomberg; 462 import mir.ion.ser.ion; 463 import mir.ion.ser.json; 464 import mir.ion.ser.text; 465 BloombergSerializer!() ser; 466 BloombergElement* value; 467 serializeValue(ser, value.init); 468 auto text = value.serializeText; 469 auto json = value.serializeJson; 470 auto ion = value.serializeIon; 471 }