diff --git a/apps/OpenSpace/CMakeLists.txt b/apps/OpenSpace/CMakeLists.txt index c5e93b08bb..b4ffa29792 100644 --- a/apps/OpenSpace/CMakeLists.txt +++ b/apps/OpenSpace/CMakeLists.txt @@ -96,7 +96,7 @@ add_executable(${APPLICATION_NAME} ${RESOURCE_FILE} ) if (OPENSPACE_SPOUT_SUPPORT) - copy_files( + ghl_copy_files( ${APPLICATION_NAME} ${OPENSPACE_APPS_DIR}/OpenSpace/ext/spout/SpoutLibrary.dll ) diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index ec55894de0..d816e22ed1 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -42,10 +42,16 @@ #include #include -#include #include #include +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable : 4091) +#include +#pragma warning (pop) +#endif // _MSC_VER + #endif // WIN32 #ifdef OPENVR_SUPPORT diff --git a/data/scene/globebrowsing.scene b/data/scene/globebrowsing.scene index 2de6f66138..1ae95684b4 100644 --- a/data/scene/globebrowsing.scene +++ b/data/scene/globebrowsing.scene @@ -95,6 +95,7 @@ return { "sun", "stars", "milkyway", + -- "satellites" } } diff --git a/data/scene/lodglobes/earth/earth.mod b/data/scene/lodglobes/earth/earth.mod index 9fb58723d4..46ec1a9d58 100644 --- a/data/scene/lodglobes/earth/earth.mod +++ b/data/scene/lodglobes/earth/earth.mod @@ -14,6 +14,18 @@ return { }, }, }, + { + -- The default reference frame for Earth-orbiting satellites + Name = "EarthInertial", + Parent = "EarthBarycenter", + Transform = { + Rotation = { + Type = "SpiceRotation", + SourceFrame = "J2000", + DestinationFrame = "GALACTIC", + } + }, + }, -- EarthTrail module { Name = "EarthTrail", diff --git a/data/scene/satellites.scene b/data/scene/satellites.scene new file mode 100644 index 0000000000..20eed4c4d4 --- /dev/null +++ b/data/scene/satellites.scene @@ -0,0 +1,71 @@ +function preInitialization() + --[[ + The scripts in this function are executed after the scene is loaded but before the + scene elements have been initialized, thus they should be used to set the time at + which the scene should start and other settings that might determine initialization + critical objects. + ]]-- + + openspace.spice.loadKernel("${SPICE}/naif0011.tls") + openspace.spice.loadKernel("${SPICE}/pck00010.tpc") + -- openspace.spice.loadKernel("${OPENSPACE_DATA}/spice/de430_1850-2150.bsp") + + openspace.time.setTime(openspace.time.currentWallTime()) + + --Test for vernal equinox time 2017 + --openspace.time.setTime("2017 MAR 20 10:28:30.500") + + dofile(openspace.absPath('${SCRIPTS}/bind_keys_satellites.lua')) +end + +function postInitialization() + --[[ + The scripts in this function are executed after all objects in the scene have been + created and initialized, but before the first render call. This is the place to set + graphical settings for the renderables. + ]]-- + openspace.printInfo("Setting default values") + openspace.setPropertyValue("SunMarker.renderable.enabled", false) + openspace.setPropertyValue("SunGlare.renderable.enabled", false) + openspace.setPropertyValue("MilkyWay.renderable.enabled", false) + openspace.setPropertyValue("EarthMarker.renderable.enabled", false) + openspace.setPropertyValue("EarthTrail.renderable.enabled", false) + openspace.setPropertyValue("Earth.renderable.performShading", false) + + openspace.resetCameraDirection() + + openspace.printInfo("Done setting default values") + + if openspace.modules.isLoaded("ISWA") then + + openspace.iswa.addCdfFiles("${OPENSPACE_DATA}/cdflist.json"); + + --openspace.iswa.addCygnet(7); + + --openspace.iswa.addCygnet(-4,"Data","Gm"); + --openspace.iswa.addCygnet(-5,"Data","Gm"); + --openspace.iswa.addCygnet(-6,"Data","Gm"); + --openspace.iswa.addCygnet(-7,"Data","Gm"); + --openspace.iswa.addCygnet(-8,"Data","Gm"); + --openspace.iswa.addCygnet(-9,"Data","Gm"); + end +end + +return { + ScenePath = ".", + CommonFolder = "common", + Camera = { + Focus = "Earth", + Position = {-54343425747.129051, -73298476295.934555, 116584089130.590012}, + Rotation = {-0.078983, 0.830093, 0.014241, -0.551819}, + }, + Modules = { + --"satellites/earth", + "sun", + "earth", + -- "stars", + -- "milkyway", + "satellites" + } +} + diff --git a/data/scene/satellites/generate_raw.lua b/data/scene/satellites/generate_raw.lua new file mode 100644 index 0000000000..5b10dc796c --- /dev/null +++ b/data/scene/satellites/generate_raw.lua @@ -0,0 +1,66 @@ +#!/bin/lua + +--For debug purposes, but also for generating raw output in case the parsing +-- isn't working as intended. +function tableLength(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end + +output = dofile("satellites.mod") +outputLen = tableLength(output) + +print("return {") +for i=1,outputLen do + type=output[i].Renderable.Type + print(" {") + if( type == "RenderablePlane" ) then + print(" Name = \"" .. output[i].Name .. "\",") + print(" Parent = \"" .. output[i].Parent .. "\",") + print(" Renderable = {") + print(" Type = \"" .. output[i].Renderable.Type .. "\",") + print(" Size = {".. output[i].Renderable.Size[1] .. "," .. output[i].Renderable.Size[2] .. "},") + print(" Origin = \"".. output[i].Renderable.Origin .. "\",") + print(" Body = \"" .. output[i].Renderable.Body .. "\",") + if (output[i].Renderable.Billboard) then + print(" Billboard = true,") + else + print(" Billboard = false,") + end + print(" Texture = \"" .. output[i].Renderable.Texture .. "\",") + print(" },") + print(" Transform = {") + print(" Translation = {") + print(" Type = \"" .. output[i].Transform.Translation.Type .. "\",") + print(" Body = \"" .. output[i].Transform.Translation.Body .. "\",") + print(" Observer = \"" .. output[i].Transform.Translation.Observer .. "\",") + print(" File = \"" .. output[i].Transform.Translation.File .. "\",") + print(" LineNum = " .. output[i].Transform.Translation.LineNum .. ",") + print(" },") + print(" Scale = {") + print(" Type = \"" .. output[i].Transform.Scale.Type .. "\",") + print(" Scale = " .. output[i].Transform.Scale.Scale .. ",") + print(" },") + print(" },") + elseif( type == "RenderableTrailOrbit" ) then + print(" Name = \"" .. output[i].Name .. "\",") + print(" Parent = \"" .. output[i].Parent .. "\",") + print(" Renderable = {") + print(" Type = \"" .. output[i].Renderable.Type .. "\",") + print(" Translation = {") + print(" Type = \"" .. output[i].Renderable.Translation.Type .. "\",") + print(" Body = \"" .. output[i].Renderable.Translation.Body .. "\",") + print(" Observer = \"" .. output[i].Renderable.Translation.Observer .. "\",") + print(" File = \"" .. output[i].Renderable.Translation.File .. "\",") + print(" LineNum = " .. output[i].Renderable.Translation.LineNum .. ",") + print(" },") + print(" Color = {" .. output[i].Renderable.Color[1] .. "," .. output[i].Renderable.Color[2] .. "," .. output[i].Renderable.Color[3] .. "},") + print(" Period = " .. output[i].Renderable.Period .. ",") + print(" Resolution = " .. output[i].Renderable.Resolution .. ",") + print(" },") + print(" GuiName = \"" .. output[i].GuiName .. "\",") + end + print(" },") +end +print("}") diff --git a/data/scene/satellites/satB.png b/data/scene/satellites/satB.png new file mode 100644 index 0000000000..5e3c14524c Binary files /dev/null and b/data/scene/satellites/satB.png differ diff --git a/data/scene/satellites/satellites.mod b/data/scene/satellites/satellites.mod new file mode 100644 index 0000000000..427642b95a --- /dev/null +++ b/data/scene/satellites/satellites.mod @@ -0,0 +1,169 @@ +DOWNLOAD = false + +function dirListing(dirname) + f = io.popen('ls ' .. dirname) + files = {} + for name in f:lines() do + table.insert(files, name) + end + return files +end + +function values(t) + local i = 0 + return function () i = i + 1; return t[i] end +end + +function trimString(s) + s = s:gsub("^%s*(.-)%s*$", "%1") + s = s:gsub("%s+", "_") + s = s:gsub("[%-()]", "") + return s +end + +function getPeriodFromFile(line2) + return tonumber(string.sub(line2, 53, 63)) +end + +function getNumLinesInFile(filename) + local ctr = 0 + for _ in io.lines(filename) do + ctr = ctr + 1 + end + return ctr +end + +function isEmpty(s) + return s == nil or s == '' +end + +--Check format of a set of 3 TLE file lines and return nonzero if there is a format error +function checkTleFileFormat(lineArr) + if isEmpty(lineArr[1]) or isEmpty(lineArr[2]) or isEmpty(lineArr[3]) then + return -1 + end + if string.sub(lineArr[2], 1, 2) ~= "1 " then + return -1 + end + if string.sub(lineArr[3], 1, 2) ~= "2 " then + return -1 + end + return 0 +end + + +function getSat(title, file, lineNum) + return { + Name = title, + Parent = "EarthInertial", + Renderable = { + Type = "RenderablePlane", + Size = 3e4, + Origin = "Center", + Body = "TLE", + Billboard = true, + Texture = "satB.png" + }, + Transform = { + Translation = { + Type = "TLETranslation", + Body = title, + Observer = "EarthInertial", + File = file, + LineNum = lineNum + }, + Scale = { + Type = "StaticScale", + Scale = 1, + } + } + } +end + +function getSatTrail(title, file, lineNum, per, color) + trailName = title .. "_trail" + + return { + Name = trailName, + Parent = "EarthInertial", + Renderable = { + Type = "RenderableTrailOrbit", + Translation = { + Type = "TLETranslation", + Body = title, + Observer = "EarthInertial", + File = file, + LineNum = lineNum + }, + Color = color, + Period = per, + Resolution = 160 + }, + GuiName = "/Satellites/" .. trailName + } +end + +------------------------------------------------------------- +--Name, URL, and color scheme for each satellite group +satelliteGroups = { + { title = "GPS", + url = "http://celestrak.com/NORAD/elements/gps-ops.txt", + trailColor = {0.9, 0.5, 0.0} + }, + { title = "SpaceStations", + url = "http://celestrak.com/NORAD/elements/stations.txt", + trailColor = {0.9, 0.0, 0.0} + }, + { title = "Geostationary", + url = "http://celestrak.com/NORAD/elements/geo.txt", + trailColor = {0.9, 0.9, 0.0} + }, +} + +modElements = {} +fileErr = "" +for sOrbit in values(satelliteGroups) do + filename = sOrbit.url:match("([^/]+)$") + filenameSansExt = filename:gsub(filename:match "(%.%w+)$", "") + sOrbit.path = "satellites/tle/" .. filename + + if DOWNLOAD then + openspace.downloadFile(sOrbit.url, sOrbit.path) + end + sOrbit.path = "../" .. sOrbit.path + pathFromScenegraphParent = "./" .. sOrbit.path + + line = {} + myfile = io.open(sOrbit.path, "r") + lines = getNumLinesInFile(sOrbit.path) + --now loop through the tle file and get each set of 3 lines + if myfile then + for n=1,lines,3 do + line[1] = myfile:read('*l') --title line + line[2] = myfile:read('*l') + line[3] = myfile:read('*l') + if( checkTleFileFormat(line) == 0 ) then + title = trimString(line[1]) + per = getPeriodFromFile(line[3]) + per = 1.0 / per * 2 --trail for 2x a single revolution + table.insert(modElements, getSat(filenameSansExt .. "_" .. title, pathFromScenegraphParent, n)) + table.insert(modElements, getSatTrail(filenameSansExt .. "_" .. title, + pathFromScenegraphParent, n, per, sOrbit.trailColor)) + else + fileErr = " TLE file syntax error on line " .. n .. ": " .. sOrbit.path + break + end + end + else + fileErr = " File not found: " .. sOrbit.path + break + end +end +assert(fileErr == "", fileErr) + + +if (fileErr == "") then + return modElements +else + return "Invalid file: " .. fileErr +end diff --git a/data/scene/satellites/tle/geo.txt b/data/scene/satellites/tle/geo.txt new file mode 100644 index 0000000000..131d1a11aa --- /dev/null +++ b/data/scene/satellites/tle/geo.txt @@ -0,0 +1,1332 @@ +TDRS 3 +1 19548U 88091B 17157.86807803 -.00000309 00000-0 00000-0 0 9997 +2 19548 14.4416 8.0727 0035139 315.3143 182.2791 1.00277792 92347 +SKYNET 4C +1 20776U 90079A 17157.24407692 .00000117 00000-0 00000-0 0 9991 +2 20776 13.8245 16.2209 0003137 48.2774 311.7180 1.00276680 97853 +TDRS 5 +1 21639U 91054B 17157.76236800 .00000078 00000-0 00000-0 0 9998 +2 21639 14.3705 21.5279 0024207 350.4285 350.3256 1.00280423 94648 +TDRS 6 +1 22314U 93003B 17157.86825340 -.00000301 00000-0 00000-0 0 9999 +2 22314 13.8980 24.4494 0003763 305.2309 192.4426 1.00268641 89385 +ASTRA 1D +1 23331U 94070A 17157.42011801 -.00000298 00000-0 00000-0 0 9990 +2 23331 7.5473 49.3635 0000659 296.5264 4.6759 1.00104533 83216 +BRASILSAT B2 +1 23536U 95016A 17157.69941272 -.00000285 00000-0 00000-0 0 9997 +2 23536 7.1712 51.2584 0002332 20.9281 6.8855 1.00271709 81251 +AMSC 1 +1 23553U 95019A 17156.69144898 -.00000124 00000-0 00000-0 0 9999 +2 23553 10.0698 39.9829 0003984 11.9967 347.9227 1.00271961 81090 +TDRS 7 +1 23613U 95035B 17157.60700767 -.00000209 00000-0 00000-0 0 9993 +2 23613 14.9710 15.2291 0030718 2.0194 181.2233 1.00269402 80185 +ECHOSTAR 1 +1 23754U 95073A 17157.49163003 -.00000246 00000-0 00000-0 0 9995 +2 23754 0.7924 90.2435 0002974 350.0571 274.5284 1.00272032 78375 +INMARSAT 3-F1 +1 23839U 96020A 17157.85035990 .00000002 00000-0 00000-0 0 9994 +2 23839 3.9663 67.5573 0005447 7.0278 191.4687 1.00272109 77250 +ASTRA 1F +1 23842U 96021A 17157.91317255 .00000135 00000-0 00000-0 0 9990 +2 23842 0.0562 314.1346 0004059 128.6019 185.7968 1.00272030 19280 +MSAT M1 +1 23846U 96022A 17156.72760441 -.00000092 00000-0 00000-0 0 9995 +2 23846 7.7361 48.8942 0005104 25.5910 334.2903 1.00272898 77348 +INMARSAT 3-F2 +1 24307U 96053A 17157.49688167 -.00000129 00000-0 00000-0 0 9992 +2 24307 2.6068 77.7716 0005484 340.3412 0.3761 1.00272172 75958 +AMC-1 (GE-1) +1 24315U 96054A 17156.88615406 .00000052 00000-0 00000-0 0 9994 +2 24315 1.5468 85.1195 0003085 352.0953 7.1338 1.00272423 13257 +AFRICASAT-2 (MEASAT-2) +1 24653U 96063B 17157.54179772 -.00000234 00000-0 00000-0 0 9997 +2 24653 7.4699 49.7796 0006474 178.0401 10.3776 1.00272601 75398 +INMARSAT 3-F3 +1 24674U 96070A 17157.76269751 .00000032 00000-0 00000-0 0 9994 +2 24674 3.4130 71.8223 0005433 3.1247 273.0973 1.00274605 74907 +AMC-2 (GE-2) +1 24713U 97002A 17157.30467139 -.00000217 00000-0 00000-0 0 9990 +2 24713 4.3187 66.0034 0005466 6.7557 206.9821 1.00273283 25250 +INTELSAT 26 (IS-26) +1 24732U 97007A 17157.92907338 -.00000004 00000-0 00000-0 0 9998 +2 24732 7.4468 50.1090 0005481 8.4977 235.6218 1.00274085 39676 +GALAXY 25 (G-25) +1 24812U 97026A 17156.55276483 -.00000169 00000-0 00000-0 0 9998 +2 24812 0.0263 331.2615 0003489 101.7437 287.0330 1.00270031 73285 +ABS-3 +1 24901U 97042A 17157.82985428 -.00000197 00000-0 00000-0 0 9993 +2 24901 4.8711 63.3290 0006867 355.5837 220.6103 1.00271955 72480 +INTELSAT 5 (IS-5) +1 24916U 97046A 17157.05344110 -.00000149 00000-0 00000-0 0 9992 +2 24916 3.5265 71.1570 0003183 356.4821 3.1426 1.00272346 72550 +AMC-3 (GE-3) +1 24936U 97050A 17157.39970170 -.00000264 00000-0 00000-0 0 9995 +2 24936 0.3597 92.2509 0003074 345.4247 249.2166 1.00272581 19365 +NSS-5 +1 24957U 97053A 17157.76527155 .00000103 00000-0 00000-0 0 9994 +2 24957 3.8209 69.3592 0003661 358.6132 153.3551 1.00271660 72116 +ECHOSTAR 3 +1 25004U 97059A 17157.68394641 -.00000287 00000-0 00000-0 0 9991 +2 25004 2.1365 80.3085 0001609 11.9974 347.3946 1.00272496 71994 +JCSAT-1B +1 25067U 97075A 17157.65122240 -.00000213 00000-0 00000-0 0 9998 +2 25067 5.5436 58.9740 0002484 352.1285 228.5799 1.00272836 71483 +ASTRA 1G +1 25071U 97076A 17157.76538498 .00000102 00000-0 00000-0 0 9998 +2 25071 2.3038 78.4519 0003655 354.3072 149.2269 1.00273075 71499 +BRASILSAT B3 +1 25152U 98006A 17156.65845583 -.00000290 00000-0 00000-0 0 9994 +2 25152 4.0090 68.4273 0002828 3.0590 356.6200 1.00273130 10445 +INMARSAT 3-F5 +1 25153U 98006B 17157.25743942 -.00000293 00000-0 00000-0 0 9991 +2 25153 2.5563 74.2286 0005365 0.4178 218.8480 1.00268538 70828 +NSS-806 +1 25239U 98014A 17157.57688493 -.00000283 00000-0 00000-0 0 9998 +2 25239 0.0704 41.6431 0004502 32.6989 341.0322 1.00272485 14235 +CHINASAT 5A (ZX 5A) +1 25354U 98033A 17157.64465316 -.00000358 00000-0 00000-0 0 9995 +2 25354 1.2212 87.1786 0001287 274.3863 251.2674 1.00268487 74179 +THOR III +1 25358U 98035A 17156.46990006 -.00000052 00000-0 00000-0 0 9990 +2 25358 5.4063 59.1747 0001643 6.5467 353.2248 1.00271491 69779 +INTELSAT 805 (IS-805) +1 25371U 98037A 17157.58191515 -.00000035 00000-0 00000-0 0 9990 +2 25371 0.0103 314.0624 0003659 127.6349 191.9694 1.00272663 69482 +ASTRA 2A +1 25462U 98050A 17157.88659128 -.00000363 00000-0 00000-0 0 9992 +2 25462 0.0759 256.1314 0002007 177.7985 254.2102 1.00268909 13282 +AFRISTAR +1 25515U 98063A 17157.75189699 .00000119 00000-0 00000-0 0 9990 +2 25515 3.2821 69.7456 0003679 5.8761 111.3929 1.00270803 68062 +JCSAT-4A +1 25630U 99006A 17157.82921876 -.00000331 00000-0 00000-0 0 9992 +2 25630 2.2754 80.0941 0000925 328.3183 270.6482 1.00579415 66939 +SKYNET 4E +1 25639U 99009B 17157.80109448 .00000014 00000-0 00000-0 0 9992 +2 25639 10.1255 28.6832 0002187 51.5487 109.5193 1.00270428 66956 +ASIASAT 3S +1 25657U 99013A 17157.61576939 -.00000242 00000-0 00000-0 0 9993 +2 25657 2.4890 78.2975 0003096 342.3571 202.2375 1.00269091 66754 +NIMIQ 1 +1 25740U 99027A 17157.39957437 -.00000203 00000-0 00000-0 0 9999 +2 25740 0.0106 324.8530 0006101 92.4300 255.0543 1.00273030 66134 +ASTRA 1H +1 25785U 99033A 17157.97189061 .00000133 00000-0 00000-0 0 9990 +2 25785 3.8060 69.5124 0002962 358.6213 220.7807 1.00270882 18351 +TELKOM 1 +1 25880U 99042A 17157.87353010 -.00000349 00000-0 00000-0 0 9992 +2 25880 0.0476 125.4053 0001661 348.3920 204.1525 1.00275014 65320 +ABS-7 +1 25894U 99046A 17157.75623596 -.00000367 00000-0 00000-0 0 9997 +2 25894 0.0142 11.5448 0002770 67.3211 204.8136 1.00266504 64071 +ABS-6 +1 25924U 99053A 17156.85154310 -.00000126 00000-0 00000-0 0 9995 +2 25924 0.0384 323.6013 0002447 117.1377 279.2886 1.00273349 64811 +TELSTAR 12 (ORION 2) +1 25949U 99059A 17157.76511053 -.00000066 00000-0 00000-0 0 9993 +2 25949 0.9151 88.8354 0003209 346.3667 346.3914 1.00272197 64461 +AMC-4 (GE-4) +1 25954U 99060A 17156.48039619 -.00000280 00000-0 00000-0 0 9990 +2 25954 0.0442 284.7377 0002671 157.0802 278.2116 1.00270511 10661 +GALAXY 11 (G-11) +1 26038U 99071A 17157.91317255 .00000133 00000-0 00000-0 0 9996 +2 26038 0.0088 145.9068 0000374 257.2525 225.9702 1.00272198 64043 +HISPASAT 1C +1 26071U 00007A 17157.52223545 .00000048 00000-0 00000-0 0 9994 +2 26071 0.5428 89.4925 0002589 72.6119 170.7654 0.99201181 14572 +SUPERBIRD-B2 +1 26095U 00012A 17157.67454814 -.00000097 00000-0 00000-0 0 9992 +2 26095 0.3791 94.5663 0003041 339.8576 225.6408 1.00271692 63369 +ASIASTAR +1 26107U 00016A 17157.86893387 -.00000337 00000-0 00000-0 0 9990 +2 26107 1.6532 81.9634 0004341 354.1170 237.1785 1.00269745 62868 +EUTELSAT 16C +1 26243U 00019A 17157.75174454 .00000086 00000-0 00000-0 0 9999 +2 26243 4.2466 66.3709 0002418 11.9963 102.0901 1.00273976 62686 +EUTELSAT 80A +1 26369U 00028A 17157.85005696 .00000026 00000-0 00000-0 0 9994 +2 26369 0.9945 85.2151 0005047 330.7769 212.9123 1.00032976 62374 +TDRS 8 +1 26388U 00034A 17157.19884633 -.00000238 00000-0 00000-0 0 9991 +2 26388 7.4759 55.9283 0003614 246.3223 113.4765 1.00270515 62106 +ECHOSTAR 6 +1 26402U 00038A 17157.54494727 -.00000154 00000-0 00000-0 0 9999 +2 26402 4.1671 67.1693 0003849 10.8801 277.0633 1.00272694 61847 +INTELSAT 9 (IS-9) +1 26451U 00043A 17157.57202302 -.00000265 00000-0 00000-0 0 9991 +2 26451 3.5142 71.3824 0003799 350.3295 358.0679 1.00365439 11073 +BRASILSAT B4 +1 26469U 00046A 17156.78370957 -.00000176 00000-0 00000-0 0 9998 +2 26469 1.6370 85.2597 0003484 349.2296 10.0438 1.00271930 61899 +NILESAT 102 +1 26470U 00046B 17157.54216150 -.00000064 00000-0 00000-0 0 9995 +2 26470 1.6601 83.7061 0003857 345.8061 13.7579 1.00272468 12139 +ASTRA 2B +1 26494U 00054A 17157.68035657 .00000067 00000-0 00000-0 0 9998 +2 26494 2.1910 79.0928 0002631 287.8567 148.5516 1.00093376 13823 +AMC-7 (GE-7) +1 26495U 00054B 17157.66591004 .00000083 00000-0 00000-0 0 9998 +2 26495 0.0425 70.3004 0000575 293.2921 356.4085 1.00272353 61199 +NSS-11 (AAP-1) +1 26554U 00059A 17157.75422963 -.00000349 00000-0 00000-0 0 9992 +2 26554 0.0206 311.3634 0002835 132.7724 190.9234 1.00270805 8242 +N-SAT-110 (JCSAT-110) +1 26559U 00060A 17157.96923611 -.00000355 00000-0 00000-0 0 9995 +2 26559 0.0511 101.0793 0001551 336.4429 277.0289 1.00272458 8215 +AMC-6 (GE-6) +1 26580U 00067A 17157.36854942 -.00000206 00000-0 00000-0 0 9995 +2 26580 0.0431 293.8127 0000377 175.2322 198.9257 1.00016707 60911 +INTELSAT 12 (IS-12) +1 26590U 00068A 17157.75722942 .00000132 00000-0 00000-0 0 9991 +2 26590 0.4642 93.2981 0002778 18.8500 100.8023 1.00273874 18654 +INTELSAT 1R (IS-1R) +1 26608U 00072A 17157.82344980 -.00000051 00000-0 00000-0 0 9997 +2 26608 0.2962 92.8891 0001314 76.9935 193.6059 1.00082695 60592 +ANIK F1 +1 26624U 00076A 17157.58929326 -.00000078 00000-0 00000-0 0 9994 +2 26624 0.0073 330.8252 0002626 129.0283 260.1762 1.00268795 60666 +ASTRA 2D +1 26638U 00081A 17157.31978469 .00000042 00000-0 00000-0 0 9998 +2 26638 3.6306 70.1028 0001749 3.1681 356.4654 1.00270542 19849 +AMC-8 (GE-8) +1 26639U 00081B 17157.50931538 .00000099 00000-0 00000-0 0 9998 +2 26639 0.0155 1.7174 0003007 70.9878 226.7467 1.00273654 60208 +SKYNET 4F +1 26695U 01005B 17156.48174351 -.00000258 00000-0 00000-0 0 9993 +2 26695 8.8865 36.7282 0002854 21.9931 334.8059 1.00273913 59807 +EUTELSAT 33C +1 26719U 01011A 17157.91808519 .00000151 00000-0 00000-0 0 9997 +2 26719 0.0657 14.7416 0004077 45.4654 198.8809 1.00272805 59488 +INTELSAT 10 (IS-10) +1 26766U 01019A 17157.91226983 .00000122 00000-0 00000-0 0 9994 +2 26766 1.4974 85.2555 0002810 328.5020 217.6481 1.00273208 12572 +INTELSAT 901 (IS-901) +1 26824U 01024A 17157.56229900 -.00000146 00000-0 00000-0 0 9995 +2 26824 0.0314 72.5832 0002605 339.3795 27.6129 1.00271212 58636 +ASTRA 2C +1 26853U 01025A 17157.85283747 .00000042 00000-0 00000-0 0 9993 +2 26853 0.3795 91.4600 0003533 340.7739 190.5107 1.00273957 11994 +ARTEMIS +1 26863U 01029A 17157.90349637 -.00000377 00000-0 00000-0 0 9997 +2 26863 12.7220 33.1765 0002052 60.8791 249.7556 1.00272583 11082 +INTELSAT 902 (IS-902) +1 26900U 01039A 17157.85096675 .00000028 00000-0 00000-0 0 9997 +2 26900 0.0262 33.6893 0003013 55.7823 174.2913 1.00273188 57658 +EUTELSAT 12 WEST B +1 26927U 01042A 17157.91924353 -.00000104 00000-0 00000-0 0 9996 +2 26927 0.0391 7.2969 0005667 56.4965 150.1242 1.00273270 57451 +DIRECTV 4S +1 26985U 01052A 17157.53742727 -.00000117 00000-0 00000-0 0 9996 +2 26985 0.0233 331.2984 0002305 112.5189 263.5725 1.00269707 56932 +INSAT-3C +1 27298U 02002A 17157.89595112 -.00000262 00000-0 00000-0 0 9994 +2 27298 0.6042 91.6269 0002103 228.4690 351.4248 1.00270722 56348 +ECHOSTAR 7 +1 27378U 02006A 17157.50735625 -.00000005 00000-0 00000-0 0 9995 +2 27378 0.0111 338.1778 0001931 118.0619 222.7119 1.00271538 55998 +INTELSAT 904 (IS-904) +1 27380U 02007A 17157.75723111 .00000132 00000-0 00000-0 0 9993 +2 27380 0.0571 120.8959 0002880 308.6601 143.4987 1.00272687 18706 +TDRS 9 +1 27389U 02011A 17157.72953012 -.00000105 00000-0 00000-0 0 9990 +2 27389 5.2291 79.2046 0017938 252.8065 173.8674 1.00272866 57374 +JCSAT 2A +1 27399U 02015A 17157.82602378 -.00000311 00000-0 00000-0 0 9991 +2 27399 0.4587 92.8420 0001099 40.3381 195.6030 1.00268306 55559 +ASTRA 3A +1 27400U 02015B 17156.61468224 -.00000288 00000-0 00000-0 0 9993 +2 27400 3.7937 68.8431 0003222 15.9485 343.7218 1.00274601 55655 +INTELSAT 903 (IS-903) +1 27403U 02016A 17157.57202302 -.00000243 00000-0 00000-0 0 9997 +2 27403 0.0148 323.3270 0003052 105.3474 357.9124 1.00273140 12603 +NSS-7 +1 27414U 02019A 17156.57859080 -.00000163 00000-0 00000-0 0 9993 +2 27414 1.8718 83.1197 0002435 355.0749 4.2885 1.00272047 55494 +DIRECTV 5 (TEMPO 1) +1 27426U 02023A 17156.59985321 -.00000063 00000-0 00000-0 0 9990 +2 27426 0.0250 327.0495 0003596 106.1164 286.8736 1.00269966 55236 +INTELSAT 905 (IS-905) +1 27438U 02027A 17157.56716090 -.00000189 00000-0 00000-0 0 9999 +2 27438 0.0156 297.3086 0003061 119.7567 17.7793 1.00272874 54890 +EXPRESS-A4 +1 27441U 02029A 17157.83061253 -.00000254 00000-0 00000-0 0 9992 +2 27441 6.0451 56.5744 0004021 51.2124 231.9396 1.00266835 54901 +GALAXY 3C (G-3C) +1 27445U 02030A 17155.56072771 -.00000160 00000-0 00000-0 0 9999 +2 27445 0.0146 313.9998 0002083 317.0344 88.9393 1.00268666 13494 +EUTELSAT 5 WEST A +1 27460U 02035A 17157.30598495 -.00000048 00000-0 00000-0 0 9996 +2 27460 0.0407 354.0164 0005408 77.7738 288.2692 1.00271976 54561 +N-STAR C +1 27461U 02035B 17157.67386207 -.00000317 00000-0 00000-0 0 9992 +2 27461 4.6131 64.8577 0004203 348.0196 220.9577 1.00269035 54595 +EUTELSAT 36 WEST A +1 27508U 02040A 17157.39182088 -.00000249 00000-0 00000-0 0 9994 +2 27508 0.0671 16.5719 0004714 27.6473 315.8189 1.00274510 54130 +METEOSAT-8 (MSG-1) +1 27509U 02040B 17157.91560703 .00000134 00000-0 00000-0 0 9999 +2 27509 4.8249 58.4007 0000781 321.3004 247.0559 1.00282771 54194 +INTELSAT 906 (IS-906) +1 27513U 02041A 17157.85051046 .00000010 00000-0 00000-0 0 9996 +2 27513 0.0167 304.9366 0002695 145.8257 175.0220 1.00272562 53975 +KODAMA (DRTS) +1 27516U 02042B 17157.92791715 -.00000245 00000-0 00000-0 0 9993 +2 27516 5.0249 62.1271 0003126 12.9916 245.2045 1.00267568 12844 +KALPANA-1 (METSAT 1) +1 27525U 02043A 17157.24074489 -.00000087 00000-0 00000-0 0 9995 +2 27525 6.3332 55.3089 0012521 233.7089 125.9805 1.00272053 53999 +HISPASAT 1D +1 27528U 02044A 17157.86991012 -.00000220 00000-0 00000-0 0 9996 +2 27528 0.0259 332.1791 0004154 85.5483 120.9071 1.00272546 9753 +TDRS 10 +1 27566U 02055A 17156.93899134 .00000071 00000-0 00000-0 0 9992 +2 27566 5.0166 58.4666 0007385 248.0779 111.5909 1.00270185 53166 +NSS-6 +1 27603U 02057A 17156.77498163 -.00000277 00000-0 00000-0 0 9994 +2 27603 0.0473 102.4211 0001387 313.2719 212.6914 1.00271799 53021 +NIMIQ 2 +1 27632U 02062A 17156.69497149 -.00000288 00000-0 00000-0 0 9998 +2 27632 1.9736 82.0863 0001443 250.4056 108.9664 1.00268898 11131 +INTELSAT 907 (IS-907) +1 27683U 03007A 17157.36824828 -.00000207 00000-0 00000-0 0 9998 +2 27683 0.0167 299.5781 0002888 136.1041 284.3503 1.00272710 52377 +GALAXY 12 (G-12) +1 27715U 03013B 17157.47798205 .00000054 00000-0 00000-0 0 9991 +2 27715 0.0187 320.3459 0001910 124.4962 213.2990 1.00273091 19071 +ASIASAT 4 +1 27718U 03014A 17154.76078257 -.00000373 00000-0 00000-0 0 9997 +2 27718 0.0554 107.4671 0000332 120.2394 60.6607 1.00269696 51826 +HELLAS-SAT 2 +1 27811U 03020A 17157.95948205 .00000148 00000-0 00000-0 0 9990 +2 27811 0.0173 15.2568 0004017 55.5793 209.0975 1.00271046 9793 +AMC-9 (GE-12) +1 27820U 03024A 17157.36854942 -.00000221 00000-0 00000-0 0 9996 +2 27820 0.0149 345.1473 0002670 86.0434 233.4626 1.00271443 51310 +THURAYA-2 +1 27825U 03026A 17157.77479958 .00000124 00000-0 00000-0 0 9999 +2 27825 4.9108 29.1082 0004667 32.3379 156.8443 1.00274495 51289 +OPTUS C1 +1 27831U 03028B 17157.64800564 -.00000152 00000-0 00000-0 0 9997 +2 27831 0.0147 142.7331 0003679 312.2794 189.5222 1.00269917 51256 +ECHOSTAR 12 (RAINBOW 1) +1 27852U 03033A 17157.85925034 -.00000286 00000-0 00000-0 0 9997 +2 27852 0.0153 320.1155 0002459 153.4265 30.0486 1.00271393 50921 +GALAXY 23 (G-23) +1 27854U 03034A 17157.55834939 .00000009 00000-0 00000-0 0 9992 +2 27854 0.0078 50.3401 0002591 20.5670 264.2678 1.00266961 55470 +EUTELSAT 33A +1 27948U 03043A 17157.91808519 .00000148 00000-0 00000-0 0 9995 +2 27948 2.3530 78.3719 0002192 246.4604 292.0221 1.00272390 12410 +GALAXY 13 (HORIZONS-1) +1 27954U 03044A 17157.49490420 .00000044 00000-0 00000-0 0 9999 +2 27954 0.0095 68.2889 0001794 34.0552 203.9008 1.00269771 50093 +ZHONGXING-20 +1 28082U 03052A 17157.86442207 -.00000331 00000-0 00000-0 0 9992 +2 28082 3.4896 71.3775 0002820 340.2019 258.3647 1.00271326 49698 +YAMAL 202 +1 28089U 03053A 17156.89999981 .00000113 00000-0 00000-0 0 9995 +2 28089 0.0645 108.1958 0003615 299.6727 219.6142 1.00274795 49602 +EXPRESS-AM22 (SESAT 2) +1 28134U 03060A 17157.82012833 -.00000142 00000-0 00000-0 0 9993 +2 28134 0.4310 91.3126 0000677 12.5441 166.9011 1.00271968 49211 +AMC-10 (GE-10) +1 28154U 04003A 17157.68986111 .00000083 00000-0 00000-0 0 9999 +2 28154 0.0102 324.5364 0002911 113.5854 290.5135 1.00274282 48870 +ABS-4 (MOBISAT-1) +1 28184U 04007A 17157.85283747 .00000037 00000-0 00000-0 0 9999 +2 28184 0.0179 315.6427 0001657 131.2375 176.5646 1.00271252 19275 +EUTELSAT 7A +1 28187U 04008A 17157.64713779 .00000044 00000-0 00000-0 0 9991 +2 28187 0.0661 14.8212 0004472 83.1599 37.2438 1.00275037 48320 +DIRECTV 7S +1 28238U 04016A 17157.55819626 -.00000004 00000-0 00000-0 0 9992 +2 28238 0.0157 340.2692 0003146 93.8117 262.9614 1.00273727 47995 +AMC-11 (GE-11) +1 28252U 04017A 17157.57680061 .00000064 00000-0 00000-0 0 9995 +2 28252 0.0180 353.1619 0002945 81.1891 257.4684 1.00274073 47636 +INTELSAT 10-02 +1 28358U 04022A 17157.83869049 -.00000015 00000-0 00000-0 0 9999 +2 28358 0.0120 356.1950 0000295 58.0826 142.0919 1.00269757 47582 +APSTAR 5 (TELSTAR 18) +1 28364U 04024A 17157.83092135 -.00000298 00000-0 00000-0 0 9995 +2 28364 0.0030 6.5684 0003143 80.0270 245.9630 1.00271874 47487 +ANIK F2 +1 28378U 04027A 17157.51971437 -.00000055 00000-0 00000-0 0 9990 +2 28378 0.0287 34.3513 0000292 66.2633 230.5249 1.00273695 47284 +AMAZONAS 1 +1 28393U 04031A 17157.57202302 -.00000251 00000-0 00000-0 0 9994 +2 28393 1.7315 83.7697 0004323 359.8041 341.2300 1.00271596 47001 +AMC-15 +1 28446U 04041A 17157.35743792 -.00000094 00000-0 00000-0 0 9998 +2 28446 0.0306 43.6946 0002577 24.0323 210.8459 1.00270854 46323 +AMC-16 +1 28472U 04048A 17157.39961249 -.00000211 00000-0 00000-0 0 9998 +2 28472 0.0300 46.8859 0002736 27.0149 239.9535 1.00272492 45742 +NSS-10 (AMC-12) +1 28526U 05003A 17157.57202302 -.00000255 00000-0 00000-0 0 9996 +2 28526 0.0171 331.5816 0002986 103.2954 348.7724 1.00271963 20118 +XTAR-EUR +1 28542U 05005A 17156.21444778 .00000143 00000-0 00000-0 0 9996 +2 28542 0.0436 105.4196 0002521 319.0050 295.6000 1.00270784 45101 +XM-3 (RHYTHM) +1 28626U 05008A 17157.39961249 -.00000210 00000-0 00000-0 0 9995 +2 28626 0.0068 215.5294 0000192 171.9039 286.3071 1.00266636 44993 +INMARSAT 4-F1 +1 28628U 05009A 17156.92660775 -.00000269 00000-0 00000-0 0 9990 +2 28628 2.8757 11.8827 0003527 65.6031 294.1247 1.00272126 44606 +APSTAR 6 +1 28638U 05012A 17157.83122773 -.00000322 00000-0 00000-0 0 9997 +2 28638 0.0306 58.4940 0002057 17.2640 252.9201 1.00272736 69343 +SPACEWAY 1 +1 28644U 05015A 17157.53283144 -.00000106 00000-0 00000-0 0 9994 +2 28644 0.0476 154.0547 0000531 193.5024 356.4599 1.00266303 44440 +DIRECTV 8 +1 28659U 05019A 17156.57419940 -.00000123 00000-0 00000-0 0 9991 +2 28659 0.0097 10.0881 0003284 61.1747 288.7721 1.00272405 44070 +GALAXY 28 (G-28) +1 28702U 05022A 17156.36348095 -.00000193 00000-0 00000-0 0 9990 +2 28702 0.0123 38.9577 0002187 345.0013 271.8678 1.00272171 43748 +EXPRESS-AM3 +1 28707U 05023A 17157.96047541 -.00000326 00000-0 00000-0 0 9993 +2 28707 0.0067 246.6538 0000998 196.3073 261.3603 1.00271254 43783 +THAICOM 4 +1 28786U 05028A 17157.76759243 -.00000367 00000-0 00000-0 0 9997 +2 28786 0.0135 354.6117 0002419 79.0491 217.5158 1.00271590 43264 +GALAXY 14 (G-14) +1 28790U 05030A 17157.55541164 .00000032 00000-0 00000-0 0 9991 +2 28790 0.0190 327.0194 0002682 102.3421 260.7443 1.00273905 43230 +ANIK F1R +1 28868U 05036A 17157.61521618 -.00000079 00000-0 00000-0 0 9994 +2 28868 0.0535 80.6010 0003176 336.5033 312.2768 1.00272802 13445 +GALAXY 15 (G-15) +1 28884U 05041A 17157.62737733 .00000074 00000-0 00000-0 0 9990 +2 28884 0.0095 323.7164 0001455 110.0613 274.2944 1.00274202 42555 +SYRACUSE 3A +1 28885U 05041B 17157.91226983 .00000125 00000-0 00000-0 0 9990 +2 28885 0.0123 47.9590 0003052 34.9277 188.0114 1.00271206 42698 +INMARSAT 4-F2 +1 28899U 05044A 17157.14672115 .00000004 00000-0 00000-0 0 9991 +2 28899 2.5707 11.7812 0002835 68.5546 291.1256 1.00271873 42451 +TELKOM 2 +1 28902U 05046A 17157.64800564 -.00000143 00000-0 00000-0 0 9999 +2 28902 0.0827 108.2191 0002043 326.9180 210.4049 1.00271196 42171 +SPACEWAY 2 +1 28903U 05046B 17157.53940951 -.00000132 00000-0 00000-0 0 9991 +2 28903 0.0462 140.7429 0000754 280.0859 289.4343 1.00279699 42429 +INSAT-4A +1 28911U 05049A 17157.06201003 -.00000171 00000-0 00000-0 0 9992 +2 28911 0.0380 105.4216 0001734 235.8286 18.7432 1.00272070 42024 +METEOSAT-9 (MSG-2) +1 28912U 05049B 17157.96248359 .00000057 00000-0 00000-0 0 9999 +2 28912 2.1848 71.0339 0000745 329.3322 211.1678 1.00265631 41971 +EUTELSAT 172A (GE-23) +1 28924U 05052A 17157.82344980 -.00000010 00000-0 00000-0 0 9998 +2 28924 0.0640 4.2410 0006025 69.7644 289.8344 1.00272333 19286 +ECHOSTAR 10 +1 28935U 06003A 17157.52223405 -.00000060 00000-0 00000-0 0 9997 +2 28935 0.0128 334.6690 0001561 122.9496 235.2838 1.00272215 41441 +HIMAWARI-7 (MTSAT-2) +1 28937U 06004A 17157.83045837 -.00000248 00000-0 00000-0 0 9993 +2 28937 0.0441 108.7166 0003613 332.5543 258.1068 1.00268894 41415 +SPAINSAT +1 28945U 06007A 17157.37511961 -.00000221 00000-0 00000-0 0 9991 +2 28945 0.0293 238.9210 0003133 211.3907 269.7246 1.00272988 41167 +EUTELSAT HOT BIRD 13E +1 28946U 06007B 17157.25603244 .00000082 00000-0 00000-0 0 9999 +2 28946 0.0016 224.9414 0004732 246.4182 248.6911 1.00274880 41238 +JCSAT-5A +1 29045U 06010A 17157.64299369 -.00000333 00000-0 00000-0 0 9996 +2 29045 0.0169 331.4853 0001801 104.7579 182.5102 1.00271734 40714 +ASTRA 1KR +1 29055U 06012A 17157.85991851 .00000115 00000-0 00000-0 0 9994 +2 29055 0.0465 282.5731 0001283 185.5859 116.0417 1.00270907 12662 +GOES 13 +1 29155U 06018A 17157.49990045 -.00000253 00000-0 00000-0 0 9999 +2 29155 0.1971 273.2915 0005903 163.6851 283.0905 1.00263260 10321 +EUTELSAT 113 WEST A +1 29162U 06020A 17156.60780462 -.00000044 00000-0 00000-0 0 9990 +2 29162 0.0229 128.7916 0002707 304.2820 286.9552 1.00271454 40391 +THAICOM 5 +1 29163U 06020B 17157.93664823 -.00000125 00000-0 00000-0 0 9990 +2 29163 0.0397 277.1662 0002921 155.9102 238.1599 1.00271082 40439 +GALAXY 16 (G-16) +1 29236U 06023A 17157.39925420 -.00000132 00000-0 00000-0 0 9995 +2 29236 0.0415 123.1487 0002843 321.6184 214.9466 1.00272656 40197 +EUTELSAT HOT BIRD 13B +1 29270U 06032A 17157.67673214 .00000083 00000-0 00000-0 0 9999 +2 29270 0.0640 13.7956 0004892 38.2230 99.8869 1.00275274 14264 +JCSAT-3A +1 29272U 06033A 17157.90924543 -.00000349 00000-0 00000-0 0 9998 +2 29272 0.0158 340.2173 0002301 69.5341 301.0817 1.00271640 14671 +SYRACUSE 3B +1 29273U 06033B 17157.79424205 -.00000048 00000-0 00000-0 0 9992 +2 29273 0.0116 334.5227 0004493 82.6200 118.9653 1.00273722 39657 +KOREASAT 5 (MUGUNGWHA 5) +1 29349U 06034A 17157.88670208 -.00000362 00000-0 00000-0 0 9992 +2 29349 0.0198 52.2408 0001592 61.2599 214.2556 1.00271627 8253 +ZHONGXING-22A +1 29398U 06038A 17156.94851480 -.00000327 00000-0 00000-0 0 9990 +2 29398 5.7706 57.8311 0001241 149.2261 130.6660 1.00268293 39337 +DIRECTV 9S +1 29494U 06043A 17157.53742660 -.00000118 00000-0 00000-0 0 9996 +2 29494 0.0144 345.2743 0003696 90.6367 271.5991 1.00270006 18337 +OPTUS D1 +1 29495U 06043B 17157.76302275 -.00000115 00000-0 00000-0 0 9993 +2 29495 0.0171 16.4417 0003644 55.5225 258.0832 1.00272516 39053 +XM-4 (BLUES) +1 29520U 06049A 17157.57626399 -.00000028 00000-0 00000-0 0 9996 +2 29520 0.0294 135.2494 0000467 280.6183 291.5103 1.00277015 38924 +BADR-4 +1 29526U 06051A 17157.97290778 .00000140 00000-0 00000-0 0 9991 +2 29526 0.0475 335.9603 0003641 58.1927 237.6604 1.00269975 18249 +FENGYUN 2D +1 29640U 06053A 17157.72187311 -.00000366 00000-0 10000-3 0 9996 +2 29640 4.4356 64.4357 0005259 316.6810 257.5736 1.00280861 38495 +WILDBLUE-1 +1 29643U 06054A 17157.51971192 -.00000054 00000-0 00000-0 0 9995 +2 29643 0.0427 124.0490 0001177 301.0075 265.9807 1.00271293 38475 +AMC-18 +1 29644U 06054B 17156.34682649 -.00000097 00000-0 00000-0 0 9999 +2 29644 0.0212 48.5605 0002690 21.6517 203.6505 1.00271544 38476 +MEASAT-3 +1 29648U 06056A 17157.77607686 -.00000246 00000-0 00000-0 0 9997 +2 29648 0.0544 134.4165 0003237 323.0236 168.8382 1.00272457 17408 +INSAT-4B +1 30793U 07007A 17157.78369844 -.00000262 00000-0 00000-0 0 9997 +2 30793 0.0520 278.1197 0008119 159.1606 193.7065 1.00270878 9141 +SKYNET 5A +1 30794U 07007B 17157.86214683 -.00000276 00000-0 00000-0 0 9992 +2 30794 0.0669 9.2549 0003424 69.8600 221.9497 1.00271597 37586 +ANIK F3 +1 31102U 07009A 17157.55819626 -.00000006 00000-0 00000-0 0 9993 +2 31102 0.0221 62.7142 0002439 5.1715 269.5064 1.00273137 13496 +ASTRA 1L +1 31306U 07016A 17157.85991851 .00000115 00000-0 00000-0 0 9991 +2 31306 0.0620 310.2522 0001942 67.5327 206.4174 1.00270848 36973 +GALAXY 17 (G-17) +1 31307U 07016B 17157.75267660 -.00000178 00000-0 00000-0 0 9995 +2 31307 0.0199 19.2854 0003333 52.0696 3.9542 1.00271618 36899 +ZHONGXING-6B +1 31800U 07031A 17157.60290969 -.00000367 00000-0 00000-0 0 9992 +2 31800 0.0230 52.6434 0002613 19.1105 156.0270 1.00270162 36405 +DIRECTV 10 +1 31862U 07032A 17156.57961764 -.00000110 00000-0 00000-0 0 9990 +2 31862 0.0171 229.8134 0000365 192.5210 297.6683 1.00272418 12553 +SPACEWAY 3 +1 32018U 07036A 17157.39936093 -.00000157 00000-0 00000-0 0 9997 +2 32018 0.0105 85.9695 0000327 288.7294 289.1154 1.00273225 36052 +BSAT-3A +1 32019U 07036B 17156.98760919 -.00000357 00000-0 00000-0 0 9996 +2 32019 0.0533 228.9851 0001387 295.7273 195.2915 1.00273708 17432 +INSAT-4CR +1 32050U 07037A 17157.91226983 .00000121 00000-0 00000-0 0 9992 +2 32050 0.0621 273.6058 0003315 164.6304 193.5538 1.00268840 18336 +OPTUS D2 +1 32252U 07044A 17157.83029597 -.00000187 00000-0 00000-0 0 9990 +2 32252 0.0170 311.7706 0003786 123.2105 271.3583 1.00272406 35376 +INTELSAT 11 (IS-11) +1 32253U 07044B 17157.42131497 -.00000274 00000-0 00000-0 0 9996 +2 32253 0.0215 79.4921 0001089 18.7092 265.5013 1.00269309 35462 +STAR ONE C1 +1 32293U 07056A 17156.47486952 -.00000284 00000-0 00000-0 0 9997 +2 32293 0.0179 339.0598 0002792 94.1440 286.8268 1.00271095 35033 +SKYNET 5B +1 32294U 07056B 17157.78452358 .00000137 00000-0 00000-0 0 9992 +2 32294 0.0666 355.6274 0003416 80.4168 126.8368 1.00271896 35155 +ASTRA 4A (SIRIUS 4) +1 32299U 07057A 17157.79354764 .00000028 00000-0 00000-0 0 9990 +2 32299 0.0158 4.3499 0003071 61.6161 119.9002 1.00271361 18644 +HORIZONS-2 +1 32388U 07063B 17157.82951910 -.00000187 00000-0 00000-0 0 9993 +2 32388 0.0098 359.5424 0003256 74.6363 204.7150 1.00271755 34790 +THURAYA-3 +1 32404U 08001A 17157.85732615 -.00000309 00000-0 00000-0 0 9993 +2 32404 4.0606 353.6630 0004915 73.1219 235.8036 1.00271427 34456 +EXPRESS-AM33 +1 32478U 08003A 17157.75390184 -.00000285 00000-0 00000-0 0 9999 +2 32478 0.0086 238.5799 0001616 270.5402 114.1081 1.00270725 34543 +THOR 5 +1 32487U 08006A 17157.83869049 -.00000013 00000-0 00000-0 0 9991 +2 32487 0.0156 281.8742 0002857 150.3466 124.4036 1.00274295 34240 +KIZUNA (WINDS) +1 32500U 08007A 17157.89696484 -.00000263 00000-0 00000-0 0 9992 +2 32500 0.8125 90.3689 0002920 345.1458 285.8835 1.00271207 33832 +AMC-14 +1 32708U 08011A 17156.91785617 .00000083 00000-0 00000-0 0 9995 +2 32708 19.0978 63.0141 0041500 358.6629 181.1967 1.00270864 35021 +DIRECTV 11 +1 32729U 08013A 17157.25083755 -.00000131 00000-0 00000-0 0 9997 +2 32729 0.0372 153.5491 0000269 220.5677 231.8543 1.00272501 12547 +ICO G1 +1 32763U 08016A 17157.46018359 -.00000179 00000-0 00000-0 0 9991 +2 32763 3.8428 354.8787 0003081 84.0600 248.9372 1.00271214 33391 +VINASAT-1 +1 32767U 08018A 17156.92650029 -.00000335 00000-0 00000-0 0 9997 +2 32767 0.0204 41.0751 0001934 39.4019 279.5445 1.00272775 33557 +STAR ONE C2 +1 32768U 08018B 17156.48870814 -.00000272 00000-0 00000-0 0 9995 +2 32768 0.0671 122.9474 0002024 346.7193 250.3540 1.00274515 33473 +TIANLIAN 1-01 +1 32779U 08019A 17157.82023682 -.00000144 00000-0 10000-3 0 9992 +2 32779 2.7809 76.1983 0032662 234.4128 320.1133 1.00274328 33403 +AMOS-3 +1 32794U 08022A 17157.83869049 -.00000039 00000-0 00000-0 0 9998 +2 32794 0.0154 261.8990 0001577 150.5664 140.7638 1.00272098 49314 +GALAXY 18 (G-18) +1 32951U 08024A 17156.63537712 .00000018 00000-0 00000-0 0 9997 +2 32951 0.0151 336.3800 0002935 102.9117 280.7413 1.00270821 33015 +CHINASAT 9 (ZX 9) +1 33051U 08028A 17155.32934455 -.00000258 00000-0 00000-0 0 9990 +2 33051 0.0161 341.0801 0003689 86.6287 35.9581 1.00268589 32928 +SKYNET 5C +1 33055U 08030A 17157.56229900 -.00000144 00000-0 00000-0 0 9991 +2 33055 0.0609 351.9781 0004718 100.8819 346.9248 1.00271510 32833 +TURKSAT 3A +1 33056U 08030B 17157.91500177 .00000142 00000-0 00000-0 0 9996 +2 33056 0.0566 91.3709 0005689 343.1426 192.3863 1.00274806 19297 +INTELSAT 25 (IS-25) +1 33153U 08034A 17157.37932809 -.00000229 00000-0 00000-0 0 9996 +2 33153 0.0166 338.1349 0002961 79.1305 302.7632 1.00269297 32570 +BADR-6 +1 33154U 08034B 17157.66748696 .00000140 00000-0 00000-0 0 9999 +2 33154 0.0743 51.2760 0005921 15.3876 94.8981 1.00275158 32698 +ECHOSTAR 11 +1 33207U 08035A 17156.59952255 -.00000064 00000-0 00000-0 0 9994 +2 33207 0.0528 99.8600 0003197 341.5030 278.6723 1.00272504 32584 +SUPERBIRD-C2 +1 33274U 08038A 17157.83045837 -.00000256 00000-0 00000-0 0 9991 +2 33274 0.0130 310.3152 0001601 133.2629 254.7403 1.00270812 72661 +AMC-21 +1 33275U 08038B 17157.55541164 .00000031 00000-0 00000-0 0 9995 +2 33275 0.0173 2.9638 0002983 87.0691 240.1617 1.00273272 32396 +INMARSAT 4-F3 +1 33278U 08039A 17157.58629861 -.00000145 00000-0 00000-0 0 9990 +2 33278 3.0222 1.3769 0002696 75.6386 291.2543 1.00271476 32209 +NIMIQ 4 +1 33373U 08044A 17157.36854942 -.00000225 00000-0 00000-0 0 9997 +2 33373 0.0106 46.0570 0002274 25.7256 233.8662 1.00271139 31909 +GALAXY 19 (G-19) +1 33376U 08045A 17156.56351010 -.00000146 00000-0 00000-0 0 9992 +2 33376 0.0189 310.2271 0003369 124.8513 284.9581 1.00271945 31766 +VENESAT-1 +1 33414U 08055A 17157.59285705 -.00000242 00000-0 00000-0 0 9994 +2 33414 0.0085 204.2447 0001484 265.7345 280.6349 1.00273402 31489 +ASTRA 1M +1 33436U 08057A 17156.96996942 .00000114 00000-0 00000-0 0 9999 +2 33436 0.0138 294.8713 0004901 170.0549 158.0159 1.00274166 31401 +CIEL-2 +1 33453U 08063A 17157.47798289 .00000053 00000-0 00000-0 0 9998 +2 33453 0.0098 325.2717 0003163 97.7131 235.3074 1.00272818 31091 +EUTELSAT HOT BIRD 13C +1 33459U 08065A 17157.25613886 .00000082 00000-0 00000-0 0 9993 +2 33459 0.0576 46.8414 0006473 34.2156 279.0166 1.00275065 14318 +EUTELSAT 48D +1 33460U 08065B 17157.14927659 .00000118 00000-0 00000-0 0 9999 +2 33460 0.0760 54.4942 0002013 19.4905 282.6043 1.00274429 31045 +FENGYUN 2E +1 33463U 08066A 17157.75360454 -.00000198 00000-0 00000-0 0 9993 +2 33463 2.1632 66.9548 0003261 212.3566 333.3623 1.00259862 31022 +EXPRESS-AM44 +1 33595U 09007A 17157.91924353 -.00000093 00000-0 00000-0 0 9991 +2 33595 0.0070 222.3990 0001170 247.4182 105.6063 1.00271553 30499 +NSS-9 +1 33749U 09008A 17157.76339559 .00000067 00000-0 00000-0 0 9994 +2 33749 0.0174 351.2485 0002402 76.2173 285.7820 1.00272814 30451 +EUTELSAT 33E +1 33750U 09008B 17157.91762347 .00000152 00000-0 00000-0 0 9993 +2 33750 0.0257 9.3758 0002718 102.9611 146.5826 1.00272786 30394 +TELSTAR 11N +1 34111U 09009A 17157.57202302 -.00000255 00000-0 00000-0 0 9993 +2 34111 0.0025 13.2728 0002760 51.7493 358.5206 1.00272628 30354 +EUTELSAT 10A +1 34710U 09016A 17157.96249140 .00000064 00000-0 00000-0 0 9997 +2 34710 0.0633 3.3505 0004813 70.8917 177.7955 1.00271293 8197 +SES-7 (PROTOSTAR 2) +1 34941U 09027A 17157.49923914 -.00000350 00000-0 00000-0 0 9990 +2 34941 0.0488 80.0440 0001143 201.2986 261.7544 1.00272726 29553 +MEASAT-3A +1 35362U 09032A 17157.03850047 -.00000248 00000-0 00000-0 0 9996 +2 35362 0.0448 227.8773 0001048 273.5393 218.5906 1.00272701 29178 +GOES 14 +1 35491U 09033A 17156.58341343 -.00000101 00000-0 00000-0 0 9991 +2 35491 0.2454 102.7014 0004603 117.0014 140.2619 1.00270799 29088 +SIRIUS FM-5 +1 35493U 09034A 17157.39958199 -.00000205 00000-0 00000-0 0 9992 +2 35493 0.0100 316.6200 0001231 61.8476 294.2336 1.00271929 28903 +TERRESTAR-1 +1 35496U 09035A 17156.55817255 -.00000066 00000-0 00000-0 0 9995 +2 35496 2.9032 344.2414 0002757 94.4467 265.4160 1.00271910 29109 +ASIASAT 5 +1 35696U 09042A 17157.89006208 -.00000312 00000-0 00000-0 0 9995 +2 35696 0.0352 100.7177 0001776 295.7994 279.8860 1.00273901 28609 +JCSAT-RA (JCSAT-12) +1 35755U 09044A 17156.93761316 -.00000352 00000-0 00000-0 0 9999 +2 35755 0.0661 62.5615 0001582 7.4265 290.0286 1.00272601 28551 +OPTUS D3 +1 35756U 09044B 17157.54201878 -.00000151 00000-0 00000-0 0 9997 +2 35756 0.0273 351.7635 0004121 60.9845 193.5266 1.00274675 28565 +PALAPA D +1 35812U 09046A 17156.97914051 -.00000365 00000-0 00000-0 0 9999 +2 35812 0.0136 18.9745 0002748 87.3136 253.7419 1.00272895 28616 +NIMIQ 5 +1 35873U 09050A 17157.36854942 -.00000262 00000-0 00000-0 0 9993 +2 35873 0.0172 14.4735 0002299 59.0155 241.4463 1.00271352 28295 +AMAZONAS 2 +1 35942U 09054A 17157.85448323 -.00000286 00000-0 00000-0 0 9996 +2 35942 0.0276 90.9532 0004533 11.8293 39.2946 1.00270624 28136 +COMSATBW-1 +1 35943U 09054B 17157.85096675 .00000020 00000-0 00000-0 0 9991 +2 35943 0.0503 88.9976 0002552 343.7982 191.9970 1.00271497 28244 +NSS-12 +1 36032U 09058A 17157.13414867 .00000065 00000-0 00000-0 0 9992 +2 36032 0.0414 65.0318 0002355 10.3984 284.5958 1.00272655 27882 +THOR 6 +1 36033U 09058B 17157.83869049 -.00000014 00000-0 00000-0 0 9991 +2 36033 0.0452 132.8724 0002159 307.5934 116.0491 1.00273364 27976 +INTELSAT 14 (IS-14) +1 36097U 09064A 17157.41670870 -.00000278 00000-0 00000-0 0 9996 +2 36097 0.0148 325.1446 0002797 106.8970 287.9895 1.00272855 27637 +EUTELSAT 36B +1 36101U 09065A 17157.91762347 .00000151 00000-0 00000-0 0 9998 +2 36101 0.0632 1.4658 0004225 73.3033 186.9720 1.00271862 27661 +INTELSAT 15 (IS-15) +1 36106U 09067A 17157.82951986 -.00000190 00000-0 00000-0 0 9998 +2 36106 0.0098 1.9457 0002487 70.1153 207.1345 1.00272292 8240 +DIRECTV 12 +1 36131U 09075A 17156.72800127 -.00000109 00000-0 00000-0 0 9993 +2 36131 0.0275 159.6681 0000566 199.0592 54.8869 1.00270150 27317 +BEIDOU G1 +1 36287U 10001A 17157.89712527 -.00000288 00000-0 00000-0 0 9999 +2 36287 1.4884 359.4741 0002313 160.5357 198.5265 1.00272476 27107 +RADUGA-1M 2 +1 36358U 10002A 17156.06102971 -.00000193 00000-0 00000-0 0 9998 +2 36358 0.0197 278.0107 0002278 180.8803 261.7570 1.00273357 26932 +SDO +1 36395U 10005A 17154.53430946 -.00000039 00000-0 00000-0 0 9997 +2 36395 28.7501 140.6633 0000222 100.1228 102.0693 1.00272472 26930 +INTELSAT 16 (IS-16) +1 36397U 10006A 17157.39494062 -.00000290 00000-0 00000-0 0 9995 +2 36397 0.0100 319.2844 0001807 116.2508 263.5401 1.00272891 26662 +GOES 15 +1 36411U 10008A 17157.66643394 .00000085 00000-0 00000-0 0 9999 +2 36411 0.1816 104.1057 0001663 47.8919 208.0117 1.00264667 26611 +ECHOSTAR 14 +1 36499U 10010A 17157.55819626 -.00000004 00000-0 00000-0 0 9991 +2 36499 0.0147 313.2379 0002913 120.6589 263.3049 1.00272444 26468 +SES-1 +1 36516U 10016A 17157.53742503 -.00000119 00000-0 00000-0 0 9991 +2 36516 0.0219 30.5483 0002873 37.3979 279.6608 1.00272382 25983 +ASTRA 3B +1 36581U 10021A 17157.75195799 .00000133 00000-0 00000-0 0 9997 +2 36581 0.0391 331.8898 0002753 112.5158 105.1380 1.00273632 25816 +COMSATBW-2 +1 36582U 10021B 17157.96011799 .00000085 00000-0 00000-0 0 9994 +2 36582 0.0552 288.8943 0002570 161.1769 164.3284 1.00276949 25918 +BEIDOU G3 +1 36590U 10024A 17157.17118737 -.00000362 00000-0 00000-0 0 9998 +2 36590 1.3400 21.0961 0005900 326.7456 79.1070 1.00268923 25719 +BADR-5 +1 36592U 10025A 17157.71285259 .00000140 00000-0 00000-0 0 9998 +2 36592 0.0149 268.1660 0003147 207.7859 61.9736 1.00275049 25831 +COMS 1 +1 36744U 10032A 17157.77345323 -.00000349 00000-0 00000-0 0 9992 +2 36744 0.0223 54.6123 0001126 17.2407 230.1570 1.00272611 18024 +ARABSAT-5A +1 36745U 10032B 17157.91808519 .00000149 00000-0 00000-0 0 9997 +2 36745 0.0516 7.4356 0002774 71.0605 178.0009 1.00270875 25588 +ECHOSTAR 15 +1 36792U 10034A 17157.93114624 -.00000285 00000-0 00000-0 0 9992 +2 36792 0.0163 326.6210 0002811 120.7234 81.7356 1.00274228 25317 +NILESAT 201 +1 36830U 10037A 17157.91924353 -.00000061 00000-0 00000-0 0 9996 +2 36830 0.0557 147.5290 0006449 289.2971 142.6059 1.00272093 25309 +RASCOM-QAF 1R +1 36831U 10037B 17157.28397349 .00000013 00000-0 00000-0 0 9992 +2 36831 0.0241 4.5323 0004147 67.0781 288.4347 1.00269772 25245 +CHINASAT 6A (ZX 6A) +1 37150U 10042A 17156.38172375 -.00000362 00000-0 00000-0 0 9991 +2 37150 0.0303 76.0858 0002117 105.5893 334.7872 1.00270274 24823 +QZS-1 (MICHIBIKI) +1 37158U 10045A 17157.90776417 -.00000101 00000-0 00000-0 0 9999 +2 37158 40.8420 159.4727 0752934 270.2097 289.7799 1.00282324 24689 +XM-5 +1 37185U 10053A 17157.39960486 -.00000210 00000-0 00000-0 0 9993 +2 37185 0.0159 321.5041 0001950 37.8879 314.2652 1.00273149 24370 +BSAT-3B +1 37207U 10056B 17157.73216803 -.00000355 00000-0 00000-0 0 9996 +2 37207 0.0458 328.4181 0002832 38.4537 261.9045 1.00271999 19195 +BEIDOU G4 +1 37210U 10057A 17157.76302275 -.00000117 00000-0 00000-0 0 9999 +2 37210 0.7140 41.5129 0002592 174.3277 114.1286 1.00273985 24226 +SKYTERRA 1 +1 37218U 10061A 17157.49089657 -.00000124 00000-0 00000-0 0 9996 +2 37218 3.2345 330.5505 0003926 156.4352 203.5108 1.00273487 24161 +ZHONGXING-20A +1 37234U 10064A 17157.83107270 -.00000342 00000-0 00000-0 0 9999 +2 37234 0.0497 252.4998 0003318 154.1537 277.9389 1.00267000 23962 +HYLAS 1 +1 37237U 10065A 17157.38521387 -.00000239 00000-0 00000-0 0 9997 +2 37237 0.0389 40.1805 0001814 38.8492 281.1460 1.00270886 23942 +INTELSAT 17 (IS-17) +1 37238U 10065B 17157.89278181 -.00000006 00000-0 00000-0 0 9997 +2 37238 0.0101 291.2063 0002311 147.7385 203.9456 1.00272275 20368 +EUTELSAT KA-SAT 9A +1 37258U 10069A 17157.26711220 .00000057 00000-0 00000-0 0 9995 +2 37258 0.0333 291.8546 0002476 175.7444 252.4284 1.00278754 10981 +HISPASAT 1E +1 37264U 10070A 17157.65195397 -.00000220 00000-0 00000-0 0 9991 +2 37264 0.0706 110.0648 0004979 347.7622 2.1245 1.00271641 20128 +KOREASAT 6 +1 37265U 10070B 17157.93007414 -.00000366 00000-0 00000-0 0 9996 +2 37265 0.0092 309.0212 0002028 105.8293 291.4938 1.00270400 23716 +BEIDOU IGSO 3 +1 37384U 11013A 17157.95024447 -.00000219 00000-0 10000-3 0 9994 +2 37384 57.8138 73.0428 0026511 210.5075 74.5458 1.00264335 22616 +INTELSAT NEW DAWN +1 37392U 11016A 17157.91808519 .00000151 00000-0 00000-0 0 9992 +2 37392 0.0124 310.0296 0000824 63.2254 245.5350 1.00271368 20239 +YAHSAT 1A +1 37393U 11016B 17157.57855374 .00000096 00000-0 00000-0 0 9994 +2 37393 0.0088 304.6116 0002219 144.9465 66.3902 1.00271269 22492 +TELSTAR 14R +1 37602U 11021A 17157.95317137 -.00000284 00000-0 00000-0 0 9995 +2 37602 0.0172 352.7963 0001552 49.4524 133.4413 1.00272534 22799 +GSAT-8 +1 37605U 11022A 17157.85329606 .00000080 00000-0 00000-0 0 9995 +2 37605 0.0441 274.3293 0003400 45.0355 298.2936 1.00273271 20151 +ST-2 +1 37606U 11022B 17157.45981177 -.00000217 00000-0 00000-0 0 9992 +2 37606 0.0397 114.8699 0001056 289.8834 103.8397 1.00271415 22203 +CHINASAT 10 (ZX 10) +1 37677U 11026A 17157.96923471 -.00000356 00000-0 00000-0 0 9997 +2 37677 0.0638 276.1553 0002479 222.3704 216.4311 1.00268996 21901 +TIANLIAN 1-02 +1 37737U 11032A 17157.76269751 .00000024 00000-0 00000-0 0 9994 +2 37737 2.5713 77.5385 0004133 224.3807 44.7881 1.00275324 21659 +GSAT-12 +1 37746U 11034A 17157.90983847 -.00000169 00000-0 00000-0 0 9992 +2 37746 0.0175 312.6345 0007942 142.4166 210.9774 1.00271473 10424 +SES-3 +1 37748U 11035A 17157.53283324 -.00000106 00000-0 00000-0 0 9998 +2 37748 0.0233 312.0806 0002815 86.6892 305.1847 1.00269738 21629 +KAZSAT-2 +1 37749U 11035B 17157.54021977 -.00000202 00000-0 00000-0 0 9991 +2 37749 0.0336 243.8747 0001402 224.8646 67.3812 1.00269450 21500 +ASTRA 1N +1 37775U 11041A 17156.64834056 .00000113 00000-0 00000-0 0 9997 +2 37775 0.0509 21.2424 0005680 34.8032 90.8175 1.00275052 11463 +BSAT-3C (JCSAT-110R) +1 37776U 11041B 17157.96923750 -.00000355 00000-0 00000-0 0 9993 +2 37776 0.0173 339.7994 0000221 150.8905 223.7644 1.00270349 17439 +PAKSAT-1R +1 37779U 11042A 17157.91606583 .00000150 00000-0 00000-0 0 9996 +2 37779 0.0561 124.7736 0003650 295.9957 202.4185 1.00274039 21413 +CHINASAT 1A (ZX 1A) +1 37804U 11047A 17156.11357760 -.00000347 00000-0 00000-0 0 9990 +2 37804 0.0612 100.8674 0000325 277.4419 46.1710 1.00270138 21007 +COSMOS 2473 +1 37806U 11048A 17156.33223026 -.00000116 00000-0 00000-0 0 9998 +2 37806 0.0617 100.9188 0004391 357.4588 261.6708 1.00272552 20998 +SES-2 +1 37809U 11049A 17157.39955912 -.00000201 00000-0 00000-0 0 9990 +2 37809 0.0142 329.0445 0002694 87.8130 254.9818 1.00272084 20947 +ARABSAT-5C +1 37810U 11049B 17157.85991851 .00000119 00000-0 00000-0 0 9992 +2 37810 0.0615 358.9626 0002970 80.4993 145.5734 1.00273302 20844 +EUTELSAT 7 WEST A +1 37816U 11051A 17157.31235323 -.00000066 00000-0 00000-0 0 9999 +2 37816 0.0591 3.6728 0004397 139.9932 216.3640 1.00271238 20904 +QUETZSAT 1 +1 37826U 11054A 17157.59285705 -.00000246 00000-0 00000-0 0 9992 +2 37826 0.0168 345.0766 0003228 96.2390 310.2983 1.00272494 16853 +INTELSAT 18 (IS-18) +1 37834U 11056A 17157.76221866 .00000048 00000-0 00000-0 0 9999 +2 37834 0.0183 358.4864 0002192 77.2725 273.9864 1.00273099 20878 +EUTELSAT 16A +1 37836U 11057A 17157.75177503 .00000100 00000-0 00000-0 0 9998 +2 37836 0.0640 358.7853 0004559 80.3391 102.8677 1.00273992 20720 +VIASAT-1 +1 37843U 11059A 17157.55657872 -.00000028 00000-0 00000-0 0 9998 +2 37843 0.0236 332.0258 0002788 98.6617 269.7295 1.00270478 21207 +ASIASAT 7 +1 37933U 11069A 17157.96397713 -.00000338 00000-0 00000-0 0 9993 +2 37933 0.0339 90.0623 0001623 314.3435 303.6651 1.00269996 11791 +LUCH 5A +1 37951U 11074B 17157.76286118 -.00000048 00000-0 00000-0 0 9991 +2 37951 1.5593 181.5961 0002924 232.2936 283.0553 1.00269984 20125 +NIGCOMSAT 1R +1 38014U 11077A 17157.73438644 .00000140 00000-0 00000-0 0 9997 +2 38014 0.0313 230.4947 0000634 180.2270 151.4601 1.00273119 19964 +FENGYUN 2F +1 38049U 12002A 17157.96468103 -.00000362 00000-0 00000-0 0 9999 +2 38049 1.6969 78.1971 0003839 190.7806 85.9216 1.00278696 19824 +SES-4 +1 38087U 12007A 17157.11470229 -.00000174 00000-0 00000-0 0 9996 +2 38087 0.0170 356.7705 0002200 91.8215 185.4172 1.00271589 19202 +BEIDOU G5 +1 38091U 12008A 17157.85253652 .00000051 00000-0 00000-0 0 9995 +2 38091 2.2012 56.2652 0002344 222.1259 342.6549 1.00271392 19415 +INTELSAT 22 (IS-22) +1 38098U 12011A 17157.09228884 -.00000065 00000-0 00000-0 0 9997 +2 38098 0.0185 354.8786 0001888 85.1505 279.9922 1.00273219 18772 +APSTAR 7 +1 38107U 12013A 17157.96725832 -.00000106 00000-0 00000-0 0 9997 +2 38107 0.0373 65.1972 0002380 6.2732 248.8047 1.00272749 19103 +YAHSAT 1B +1 38245U 12016A 17157.50535265 .00000121 00000-0 00000-0 0 9998 +2 38245 0.0196 306.8057 0001866 128.0132 49.8106 1.00274079 18857 +JCSAT-13 +1 38331U 12023A 17156.94850367 -.00000363 00000-0 00000-0 0 9991 +2 38331 0.0110 308.1395 0002132 120.4353 291.4477 1.00272159 18467 +VINASAT-2 +1 38332U 12023B 17157.91601519 -.00000333 00000-0 00000-0 0 9990 +2 38332 0.0132 25.7857 0001991 60.3401 270.9879 1.00271910 13596 +NIMIQ 6 +1 38342U 12026A 17157.54443457 -.00000178 00000-0 00000-0 0 9995 +2 38342 0.0105 358.5601 0002789 64.2544 297.2147 1.00272560 18539 +CHINASAT 2A (ZX 2A) +1 38352U 12028A 17157.85699163 -.00000297 00000-0 00000-0 0 9991 +2 38352 0.0494 237.5227 0003466 135.8783 288.8087 1.00268939 18572 +INTELSAT 19 (IS-19) +1 38356U 12030A 17156.83216273 -.00000063 00000-0 00000-0 0 9993 +2 38356 0.0168 354.6506 0002633 79.7464 285.6317 1.00272104 17403 +ECHOSTAR 17 +1 38551U 12035A 17157.58873433 -.00000080 00000-0 00000-0 0 9994 +2 38551 0.0233 332.4519 0001981 103.0033 284.5667 1.00270136 18016 +METEOSAT-10 (MSG-3) +1 38552U 12035B 17157.29126091 -.00000010 00000-0 00000-0 0 9997 +2 38552 0.8697 351.3752 0002086 124.9845 243.6636 1.00265605 17823 +SES-5 +1 38652U 12036A 17157.75152347 .00000029 00000-0 00000-0 0 9992 +2 38652 0.0395 274.3756 0002074 160.0925 96.3891 1.00272923 18004 +TIANLIAN 1-03 +1 38730U 12040A 17157.26256946 .00000068 00000-0 00000-0 0 9993 +2 38730 0.1236 269.7366 0003508 194.5925 255.7104 1.00276653 17928 +INTELSAT 20 (IS-20) +1 38740U 12043A 17157.10209497 -.00000031 00000-0 00000-0 0 9990 +2 38740 0.0074 355.5859 0000369 257.1739 107.2361 1.00270572 17706 +HYLAS 2 +1 38741U 12043B 17157.20607275 .00000148 00000-0 00000-0 0 9996 +2 38741 0.0225 332.1395 0002536 109.3849 278.5043 1.00270614 17705 +INTELSAT 21 (IS-21) +1 38749U 12045A 17156.45544027 -.00000292 00000-0 00000-0 0 9998 +2 38749 0.0207 327.2182 0002224 103.5571 289.2487 1.00269618 17471 +ASTRA 2F +1 38778U 12051A 17157.21388270 .00000144 00000-0 00000-0 0 9993 +2 38778 0.0550 223.5384 0001024 294.3720 202.0940 1.00274747 17227 +GSAT-10 +1 38779U 12051B 17157.06200223 -.00000172 00000-0 00000-0 0 9990 +2 38779 0.0600 108.0287 0003098 319.0622 292.9417 1.00272826 17225 +INTELSAT 23 (IS-23) +1 38867U 12057A 17157.43886168 -.00000290 00000-0 00000-0 0 9994 +2 38867 0.0168 327.2962 0001873 101.8709 290.8530 1.00269890 17049 +BEIDOU G6 +1 38953U 12059A 17157.96657905 -.00000180 00000-0 10000-3 0 9997 +2 38953 1.6354 80.0230 0003232 164.6746 82.8829 1.00271201 16981 +LUCH 5B +1 38977U 12061A 17157.68524606 -.00000137 00000-0 00000-0 0 9994 +2 38977 3.5243 74.5915 0003594 334.5503 76.4936 1.00270463 17116 +YAMAL 300K +1 38978U 12061B 17157.76339559 .00000067 00000-0 00000-0 0 9996 +2 38978 0.0077 269.8177 0001550 295.7230 147.5796 1.00272474 16564 +STAR ONE C3 +1 38991U 12062A 17157.71332171 -.00000253 00000-0 00000-0 0 9998 +2 38991 0.0734 91.7888 0001666 340.8503 4.4402 1.00270979 16702 +EUTELSAT 21B +1 38992U 12062B 17157.23219512 .00000124 00000-0 00000-0 0 9995 +2 38992 0.0675 19.6007 0002324 63.2598 277.1661 1.00274141 16791 +ECHOSTAR 16 +1 39008U 12065A 17157.46241711 -.00000287 00000-0 00000-0 0 9999 +2 39008 0.0183 335.1454 0002154 98.7759 286.1030 1.00272183 16748 +CHINASAT 12 (ZX 12) +1 39017U 12067A 17157.45137810 -.00000212 00000-0 00000-0 0 9992 +2 39017 0.0380 80.8874 0003130 347.3808 76.7743 1.00271328 16441 +EUTELSAT 70B +1 39020U 12069A 17157.09675105 -.00000050 00000-0 00000-0 0 9992 +2 39020 0.0642 23.6083 0002690 84.5928 251.8281 1.00271618 16441 +YAMAL 402 +1 39022U 12070A 17156.83195538 .00000079 00000-0 10000-3 0 9998 +2 39022 0.0026 350.1820 0002673 86.6284 172.0224 1.00273066 16524 +SKYNET 5D +1 39034U 12075A 17157.95131024 .00000095 00000-0 00000-0 0 9994 +2 39034 0.0732 340.9460 0003806 99.6409 210.1949 1.00274362 16102 +MEXSAT 3 +1 39035U 12075B 17156.61273742 -.00000033 00000-0 00000-0 0 9994 +2 39035 0.0056 236.9808 0001770 202.6099 280.4292 1.00271446 16102 +TDRS 11 +1 39070U 13004A 17157.67865594 .00000085 00000-0 00000-0 0 9991 +2 39070 5.4081 328.6812 0008105 303.3057 56.5753 1.00265821 13633 +AMAZONAS 3 +1 39078U 13006A 17156.46368881 -.00000290 00000-0 00000-0 0 9993 +2 39078 0.0058 178.2682 0001754 277.6277 264.1241 1.00269298 11819 +AZERSPACE 1 +1 39079U 13006B 17156.16738929 .00000124 00000-0 00000-0 0 9990 +2 39079 0.0450 109.2747 0002427 335.6506 275.1008 1.00273612 15733 +EUTELSAT 117 WEST A +1 39122U 13012A 17156.61823271 -.00000020 00000-0 00000-0 0 9997 +2 39122 0.0301 334.3523 0002356 95.1526 290.5205 1.00270899 15386 +ANIK G1 +1 39127U 13014A 17157.58925234 -.00000079 00000-0 00000-0 0 9997 +2 39127 0.0122 300.7710 0001022 91.0215 328.2144 1.00272314 15190 +CHINASAT 11 (ZX 11) +1 39157U 13020A 17157.88521376 -.00000296 00000-0 00000-0 0 9990 +2 39157 0.0216 297.5545 0002161 19.5915 355.0250 1.00272870 15084 +EUTELSAT 7B +1 39163U 13022A 17157.27263000 .00000043 00000-0 00000-0 0 9998 +2 39163 0.0500 34.9279 0004218 2.0841 323.0174 1.00274777 14935 +SES-6 +1 39172U 13026A 17157.86072084 -.00000265 00000-0 00000-0 0 9994 +2 39172 0.0343 44.6534 0002271 33.2374 86.9252 1.00272899 14661 +IRNSS-1A +1 39199U 13034A 17157.45833099 .00000110 00000-0 00000-0 0 9996 +2 39199 28.8033 115.2188 0018966 189.2548 170.6083 1.00273932 14579 +ALPHASAT +1 39215U 13038A 17157.30226941 .00000131 00000-0 00000-0 0 9998 +2 39215 1.6886 29.2099 0002635 31.5775 327.7603 1.00270357 10987 +INSAT-3D +1 39216U 13038B 17157.06441889 -.00000163 00000-0 00000-0 0 9998 +2 39216 0.0121 6.4352 0000691 34.8253 318.7445 1.00271459 14239 +EUTELSAT 25B +1 39233U 13044A 17157.22143286 .00000137 00000-0 00000-0 0 9992 +2 39233 0.0521 26.6700 0001824 46.4596 286.8905 1.00271878 13578 +GSAT-7 +1 39234U 13044B 17157.08691953 -.00000084 00000-0 00000-0 0 9998 +2 39234 0.0182 263.6715 0000935 51.5189 44.8019 1.00273002 12284 +AMOS-4 +1 39237U 13045A 17157.11192798 .00000001 00000-0 00000-0 0 9997 +2 39237 0.0546 115.1014 0000970 303.3385 301.5694 1.00273651 13899 +ASTRA 2E +1 39285U 13056A 17157.21313460 .00000144 00000-0 00000-0 0 9992 +2 39285 0.0570 13.9752 0002212 49.4873 296.5603 1.00271660 13402 +SIRIUS FM-6 +1 39360U 13058A 17156.61640136 -.00000024 00000-0 00000-0 0 9992 +2 39360 0.0064 1.2561 0001302 214.3345 144.3998 1.00270910 13277 +RADUGA-1M 3 +1 39375U 13062A 17153.54222145 -.00000055 00000-0 00000-0 0 9992 +2 39375 0.0111 85.9904 0003557 1.3292 69.0907 1.00272620 13021 +SES-8 +1 39460U 13071A 17157.02889498 -.00000276 00000-0 00000-0 0 9999 +2 39460 0.0261 285.7228 0003217 152.2287 282.0846 1.00271753 11454 +INMARSAT 5-F1 +1 39476U 13073A 17157.78416375 .00000023 00000-0 00000-0 0 9990 +2 39476 0.0226 348.4229 0000042 157.5437 94.3041 1.00271057 12862 +TKSAT-1 (TUPAC KATARI) +1 39481U 13075A 17157.39955912 -.00000199 00000-0 00000-0 0 9995 +2 39481 0.0262 80.8028 0002650 325.1093 265.7352 1.00268861 12778 +EXPRESS-AM5 +1 39487U 13077A 17157.08737148 -.00000286 00000-0 00000-0 0 9995 +2 39487 0.0183 225.9272 0001087 152.4247 47.8212 1.00272613 13308 +GSAT-14 +1 39498U 14001A 17157.08704643 -.00000083 00000-0 00000-0 0 9994 +2 39498 0.0121 359.3211 0005096 75.5744 285.1609 1.00269350 12386 +THAICOM 6 +1 39500U 14002A 17157.93664877 -.00000125 00000-0 00000-0 0 9994 +2 39500 0.0699 124.7999 0003423 337.4406 209.0050 1.00271102 12387 +TDRS 12 +1 39504U 14004A 17157.33909738 -.00000280 00000-0 00000-0 0 9994 +2 39504 5.9899 336.1128 0000870 62.9722 296.9805 1.00276997 11215 +ABS-2 +1 39508U 14006A 17157.08454168 -.00000092 00000-0 00000-0 0 9995 +2 39508 0.0260 25.0067 0002927 56.4361 278.5904 1.00271774 12254 +ATHENA-FIDUS +1 39509U 14006B 17157.18733805 .00000148 00000-0 00000-0 0 9996 +2 39509 0.0240 56.4962 0001464 32.4149 271.1056 1.00273442 12002 +TURKSAT 4A +1 39522U 14007A 17157.17566243 .00000140 00000-0 00000-0 0 9993 +2 39522 0.0407 278.7972 0000805 116.4412 324.7670 1.00270948 11932 +EXPRESS-AT1 +1 39612U 14010A 17157.99154439 .00000074 00000-0 00000-0 0 9993 +2 39612 0.0243 210.4438 0000568 272.7509 185.3502 1.00273705 11983 +EXPRESS-AT2 +1 39613U 14010B 17157.08877310 -.00000288 00000-0 00000-0 0 9992 +2 39613 0.0210 217.2465 0000533 238.6254 330.6118 1.00270915 11813 +AMAZONAS 4A +1 39616U 14011A 17157.46096683 -.00000287 00000-0 00000-0 0 9993 +2 39616 0.0867 79.0306 0005518 343.3597 297.6664 1.00273138 11801 +ASTRA 5B +1 39617U 14011B 17157.91823566 .00000151 00000-0 00000-0 0 9991 +2 39617 0.0442 321.6428 0003825 122.9841 172.8998 1.00272907 11610 +IRNSS-1B +1 39635U 14017A 17156.95952933 .00000048 00000-0 00000-0 0 9996 +2 39635 29.1089 295.0821 0021490 179.1997 180.7604 1.00275528 11528 +LUCH 5V +1 39727U 14023A 17157.96502630 -.00000273 00000-0 00000-0 0 9993 +2 39727 2.7255 299.0335 0002078 147.0891 251.5913 1.00273263529465 +KAZSAT-3 +1 39728U 14023B 17157.12992527 .00000054 00000-0 00000-0 0 9996 +2 39728 0.0095 359.2657 0000563 147.0049 213.7329 1.00271437 9383 +EUTELSAT 3B +1 39773U 14030A 17157.94471205 .00000016 00000-0 00000-0 0 9997 +2 39773 0.0590 5.2305 0002365 82.6403 150.8644 1.00273880 11019 +ASIASAT 8 +1 40107U 14046A 17157.30284274 -.00000039 00000-0 00000-0 0 9991 +2 40107 0.0156 316.3110 0002166 118.5885 285.1248 1.00271407 69098 +ASIASAT 6 +1 40141U 14052A 17157.93766262 -.00000366 00000-0 00000-0 0 9990 +2 40141 0.0178 58.3054 0000886 3.9150 290.7769 1.00271863 9998 +OPTUS 10 +1 40146U 14054A 17156.83773548 -.00000081 00000-0 00000-0 0 9994 +2 40146 0.0340 82.9484 0001623 350.1925 286.8765 1.00271044 10053 +MEASAT-3B +1 40147U 14054B 17157.03852819 -.00000248 00000-0 00000-0 0 9992 +2 40147 0.0238 357.1097 0002084 48.7697 314.1376 1.00269730 10103 +LUCH (OLYMP) +1 40258U 14058A 17157.78938271 .00000063 00000-0 00000-0 0 9992 +2 40258 0.0460 143.2667 0000810 265.0279 141.1304 1.00271236 9870 +HIMAWARI-8 +1 40267U 14060A 17156.90236236 -.00000282 00000-0 00000-0 0 9994 +2 40267 0.0119 325.0622 0001214 124.4028 270.5486 1.00271594 9797 +IRNSS-1C +1 40269U 14061A 17157.75544524 -.00000164 00000-0 00000-0 0 9992 +2 40269 3.0397 249.9431 0018690 6.2154 354.2400 1.00272682 9756 +INTELSAT 30 (IS-30) +1 40271U 14062A 17156.40832028 -.00000159 00000-0 00000-0 0 9990 +2 40271 0.0114 8.1186 0001576 102.5867 195.2506 1.00272785 9687 +ARSAT 1 +1 40272U 14062B 17157.39970265 -.00000265 00000-0 00000-0 0 9994 +2 40272 0.0314 136.5216 0001345 294.2969 256.2682 1.00272031 9818 +EXPRESS-AM6 +1 40277U 14064A 17157.95131343 .00000094 00000-0 00000-0 0 9998 +2 40277 0.0134 313.3619 0001000 156.2831 181.3635 1.00271307 8526 +GSAT-16 +1 40332U 14078A 17157.13958014 .00000078 00000-0 00000-0 0 9999 +2 40332 0.0783 272.6390 0007957 162.2825 285.1671 1.00272671 9190 +DIRECTV 14 +1 40333U 14078B 17157.53940927 -.00000130 00000-0 00000-0 0 9998 +2 40333 0.0193 291.3592 0001426 68.8528 349.8871 1.00274125 9178 +YAMAL 401 +1 40345U 14082A 17152.86438141 -.00000245 00000-0 00000-0 0 9994 +2 40345 0.0664 103.2093 0000168 299.4691 248.9586 1.00269334 9297 +ASTRA 2G +1 40364U 14089A 17157.21405344 .00000144 00000-0 00000-0 0 9992 +2 40364 0.0364 31.6860 0005116 29.5303 298.8354 1.00272096 8904 +FENGYUN 2G +1 40367U 14090A 17157.97017683 -.00000331 00000-0 00000-0 0 9994 +2 40367 0.4550 257.2998 0003702 327.1910 124.6866 1.00261694 8979 +INMARSAT 5-F2 +1 40384U 15005A 17157.57688493 -.00000290 00000-0 00000-0 0 9993 +2 40384 0.0216 336.1453 0000002 138.6792 293.0412 1.00270180 8618 +ABS-3A +1 40424U 15010A 17156.30270265 -.00000034 00000-0 00000-0 0 9994 +2 40424 0.0166 334.1046 0001967 21.5779 4.3158 1.00270458 8336 +EUTELSAT 115 WEST B +1 40425U 15010B 17156.61293761 -.00000032 00000-0 00000-0 0 9993 +2 40425 0.0060 158.5821 0000506 154.2196 47.1932 1.00268158 8366 +EXPRESS-AM7 +1 40505U 15012A 17157.74217898 .00000146 00000-0 00000-0 0 9991 +2 40505 0.0070 32.4634 0002254 29.6477 140.4119 1.00274561 8298 +IRNSS-1D +1 40547U 15018A 17157.79985935 -.00000331 00000-0 00000-0 0 9999 +2 40547 29.0945 295.1992 0021169 178.4207 181.5447 1.00273872 7935 +BEIDOU I1-S +1 40549U 15019A 17157.96031042 -.00000167 00000-0 00000-0 0 9999 +2 40549 54.4117 333.8858 0040189 185.8887 173.9188 1.00295772 8010 +THOR 7 +1 40613U 15022A 17157.29387069 -.00000014 00000-0 00000-0 0 9995 +2 40613 0.0055 16.7658 0002798 58.2999 284.9653 1.00269452 7763 +TURKMENALEM52E/MONACOSAT +1 40617U 15023A 17157.14794716 .00000097 00000-0 00000-0 0 9995 +2 40617 0.0108 344.4284 0002771 86.6968 288.9050 1.00274348 7770 +DIRECTV 15 +1 40663U 15026A 17156.57931752 -.00000110 00000-0 00000-0 0 9995 +2 40663 0.0400 105.4351 0000308 356.6694 257.8980 1.00270125 7468 +SKY MEXICO-1 +1 40664U 15026B 17156.51304984 -.00000241 00000-0 00000-0 0 9996 +2 40664 0.0192 334.0846 0002191 94.4621 291.4767 1.00269981 7460 +METEOSAT-11 (MSG-4) +1 40732U 15034A 17156.97556449 -.00000031 00000-0 00000-0 0 9993 +2 40732 1.8409 241.7802 0000480 251.7722 108.9386 1.00266938 6974 +STAR ONE C4 +1 40733U 15034B 17156.48870247 -.00000272 00000-0 00000-0 0 9992 +2 40733 0.0645 72.8856 0002561 335.6664 311.4689 1.00273409 6966 +INTELSAT 34 (IS-34) +1 40874U 15039A 17157.44569474 -.00000290 00000-0 00000-0 0 9997 +2 40874 0.0253 324.4014 0000673 42.2274 353.3721 1.00270149 6611 +EUTELSAT 8 WEST B +1 40875U 15039B 17157.31420641 -.00000071 00000-0 00000-0 0 9993 +2 40875 0.0669 345.0647 0002924 77.3120 297.6536 1.00272288 6625 +GSAT-6 +1 40880U 15041A 17157.06177879 -.00000171 00000-0 00000-0 0 9997 +2 40880 0.0248 283.2072 0005968 358.2972 78.4282 1.00271204 6581 +INMARSAT 5-F3 +1 40882U 15042A 17156.79441291 .00000043 00000-0 00000-0 0 9998 +2 40882 0.0217 333.5493 0000755 130.8034 255.6557 1.00268467 6498 +TJS-1 +1 40892U 15046A 17157.67437281 -.00000160 00000-0 00000-0 0 9996 +2 40892 0.0390 94.0423 0001961 297.6627 261.4249 1.00274282 6487 +EXPRESS-AM8 +1 40895U 15048A 17156.34083615 -.00000119 00000-0 00000-0 0 9996 +2 40895 0.0188 221.4276 0000582 228.7531 272.4900 1.00273368 6305 +BEIDOU I2-S +1 40938U 15053A 17157.91354792 -.00000167 00000-0 00000-0 0 9994 +2 40938 54.2196 296.7873 0047087 172.5226 207.7175 1.00295744 6306 +SKY MUSTER (NBN1A) +1 40940U 15054A 17156.90352899 -.00000286 00000-0 00000-0 0 9994 +2 40940 0.0244 321.8934 0002086 107.5619 290.5671 1.00269508 6225 +ARSAT 2 +1 40941U 15054B 17156.51913242 -.00000232 00000-0 00000-0 0 9998 +2 40941 0.0430 126.2431 0001678 307.5160 286.2584 1.00272441 6259 +MORELOS 3 +1 40946U 15056A 17156.52375644 -.00000058 00000-0 00000-0 0 9990 +2 40946 6.4382 329.6573 0001266 194.3751 165.5754 1.00273198 6211 +APSTAR 9 +1 40982U 15059A 17156.89859662 -.00000273 00000-0 00000-0 0 9999 +2 40982 0.0437 132.3717 0000361 252.7207 334.9088 1.00271279 6097 +TURKSAT 4B +1 40984U 15060A 17157.15358557 .00000109 00000-0 00000-0 0 9997 +2 40984 0.0129 13.5932 0002306 57.1606 289.2712 1.00271214 6047 +CHINASAT 2C (ZX 2C) +1 41021U 15063A 17157.96519169 -.00000328 00000-0 00000-0 0 9994 +2 41021 0.0135 187.6740 0002205 206.8564 311.9405 1.00272905 5949 +GSAT-15 +1 41028U 15065A 17157.03303586 -.00000264 00000-0 00000-0 0 9994 +2 41028 0.0814 274.8255 0003106 130.7632 314.4368 1.00272144 5785 +BADR-7 (ARABSAT-6B) +1 41029U 15065B 17157.21998263 .00000139 00000-0 00000-0 0 9997 +2 41029 0.0441 258.8634 0002007 329.0067 132.1129 1.00271737 5685 +LAOSAT 1 +1 41034U 15067A 17156.93622780 -.00000350 00000-0 00000-0 0 9997 +2 41034 0.0174 12.3004 0001327 146.2409 201.4638 1.00267265 5801 +TELSTAR 12V +1 41036U 15068A 17157.33359668 -.00000124 00000-0 00000-0 0 9996 +2 41036 0.0227 64.3782 0002247 3.4888 292.1572 1.00272340 5555 +CHINASAT 1C (ZX 1C) +1 41103U 15073A 17157.98537590 -.00000155 00000-0 00000-0 0 9994 +2 41103 0.0095 189.8426 0002402 127.5052 14.4726 1.00273936 5609 +ELEKTRO-L 2 +1 41105U 15074A 17157.96725832 -.00000102 00000-0 00000-0 0 9995 +2 41105 0.0738 190.6773 0001945 246.5160 242.6717 1.00272238 5455 +COSMOS 2513 +1 41121U 15075A 17157.45624638 -.00000143 00000-0 00000-0 0 9995 +2 41121 0.0997 101.1679 0001968 48.3364 350.0262 1.00270866 5534 +EXPRESS-AMU1 +1 41191U 15082A 17157.91591514 .00000151 00000-0 00000-0 0 9995 +2 41191 0.0024 331.7311 0002025 93.7745 195.8078 1.00271621 5448 +GAOFEN 4 +1 41194U 15083A 17157.96397770 -.00000339 00000-0 00000-0 0 9991 +2 41194 0.0802 99.3310 0003190 117.4378 131.5242 1.00269785 5427 +BELINTERSAT-1 +1 41238U 16001A 17156.89909698 .00000100 00000-0 10000-3 0 9999 +2 41238 0.0412 131.9830 0001783 301.3605 196.3216 1.00272261 5193 +IRNSS-1E +1 41241U 16003A 17151.92699686 -.00000279 00000-0 00000-0 0 9991 +2 41241 28.7592 114.9659 0019172 183.8467 36.2942 1.00270514 5077 +INTELSAT 29E (IS-29E) +1 41308U 16004A 17157.43055619 -.00000287 00000-0 00000-0 0 9999 +2 41308 0.0402 124.8072 0002054 314.9781 280.2382 1.00272516 5118 +EUTELSAT 9B +1 41310U 16005A 17157.26699015 .00000057 00000-0 00000-0 0 9992 +2 41310 0.0496 126.1871 0001739 220.0825 13.7258 1.00274685 5054 +SES-9 +1 41380U 16013A 17156.99199875 -.00000352 00000-0 00000-0 0 9995 +2 41380 0.0171 323.9761 0001871 110.6916 285.3528 1.00272955 4681 +EUTELSAT 65 WEST A +1 41382U 16014A 17156.47536617 -.00000284 00000-0 00000-0 0 9994 +2 41382 0.0464 4.2061 0001803 67.6657 288.1467 1.00273478 4491 +IRNSS-1F +1 41384U 16015A 17156.94054837 .00000154 00000-0 00000-0 0 9993 +2 41384 4.1802 265.3994 0022688 181.7070 178.5192 1.00276072 4625 +BEIDOU IGSO 6 +1 41434U 16021A 17157.65734022 -.00000177 00000-0 10000-3 0 9996 +2 41434 55.5278 72.5149 0026618 183.6472 331.4539 1.00272722 4423 +IRNSS-1G +1 41469U 16027A 17157.67271219 -.00000337 00000-0 00000-0 0 9990 +2 41469 4.2514 266.7228 0000811 126.7626 233.4852 1.00276255 4138 +JCSAT 2B +1 41471U 16028A 17156.86540709 -.00000172 00000-0 00000-0 0 9994 +2 41471 0.0206 344.0388 0001744 97.0403 278.9406 1.00271311 4046 +THAICOM 8 +1 41552U 16031A 17156.07731655 -.00000130 00000-0 00000-0 0 9997 +2 41552 0.0755 54.9061 0004450 354.2584 310.8724 1.00271674 3708 +INTELSAT 31 (IS-31) +1 41581U 16035A 17156.55797754 -.00000158 00000-0 00000-0 0 9998 +2 41581 0.0263 266.5979 0001050 350.7469 102.6424 1.00272562 3621 +BEIDOU G7 +1 41586U 16037A 17157.77149967 -.00000255 00000-0 00000-0 0 9995 +2 41586 1.2548 317.4376 0001449 263.9468 96.1170 1.00267179 3733 +ABS-2A (MONGOLSAT-1) +1 41588U 16038A 17157.08504291 -.00000090 00000-0 00000-0 0 9994 +2 41588 0.0175 352.9121 0002063 11.1873 355.9023 1.00271473 3652 +EUTELSAT 117 WEST B +1 41589U 16038B 17156.61871427 -.00000019 00000-0 00000-0 0 9992 +2 41589 0.0011 150.6444 0000191 117.9652 91.3882 1.00273023 3638 +BRISAT +1 41591U 16039A 17156.87509979 -.00000203 00000-0 00000-0 0 9999 +2 41591 0.0197 323.0587 0002303 109.2375 287.7287 1.00271878 3561 +ECHOSTAR 18 +1 41592U 16039B 17156.46482146 -.00000290 00000-0 00000-0 0 9997 +2 41592 0.0186 349.3730 0002661 75.4375 295.2171 1.00272715 3558 +TIANTONG-1 1 +1 41725U 16048A 17157.96535390 -.00000323 00000-0 00000-0 0 9998 +2 41725 4.6001 325.3222 0003197 61.0390 318.1514 1.00268250 3213 +JCSAT-16 +1 41729U 16050A 17156.84308590 -.00000099 00000-0 00000-0 0 9991 +2 41729 0.0182 4.5057 0001977 77.2034 278.3128 1.00270643 3001 +INTELSAT 36 (IS-36) +1 41747U 16053A 17157.10224625 -.00000031 00000-0 00000-0 0 9996 +2 41747 0.0351 68.1148 0003188 305.6980 346.1959 1.00271935 2921 +INTELSAT 33E (IS-33E) +1 41748U 16053B 17157.12580693 .00000042 00000-0 00000-0 0 9990 +2 41748 0.0280 53.2312 0001987 42.9356 263.8558 1.00273968 3158 +INSAT-3DR +1 41752U 16054A 17157.08690279 -.00000083 00000-0 00000-0 0 9999 +2 41752 0.0492 273.0204 0005898 5.9511 80.9615 1.00272237 2752 +GSAT-18 +1 41793U 16060A 17157.08728701 -.00000083 00000-0 00000-0 0 9993 +2 41793 0.0522 125.5807 0008392 321.2972 273.2183 1.00272754 2478 +SKY MUSTER 2 +1 41794U 16060B 17156.89088062 -.00000251 00000-0 00000-0 0 9998 +2 41794 0.0159 317.9919 0001850 114.8702 287.1579 1.00271100 2461 +HIMAWARI-9 +1 41836U 16064A 17156.90204452 -.00000281 00000-0 00000-0 0 9997 +2 41836 0.0132 44.4867 0001500 36.3302 279.1996 1.00273071 2125 +SHIJIAN-17 (SJ-17) +1 41838U 16065A 17157.62437860 -.00000315 00000-0 00000-0 0 9999 +2 41838 0.1537 86.2147 0001778 18.5593 154.3135 1.00090507 2167 +GOES 16 +1 41866U 16071A 17156.54258151 -.00000188 00000-0 00000-0 0 9999 +2 41866 0.0014 287.1718 0000772 114.4348 318.3993 1.00261476 2035 +TIANLIAN 1-04 +1 41869U 16072A 17157.90505278 -.00000111 00000-0 00000-0 0 9995 +2 41869 2.6784 289.9420 0007110 146.7236 221.6497 1.00272396 2259 +FENGYUN 4A +1 41882U 16077A 17157.97017885 -.00000334 00000-0 00000-0 0 9991 +2 41882 0.0320 250.4568 0009426 195.4519 263.5833 1.00269626 1918 +ECHOSTAR 19 +1 41893U 16079A 17156.56377543 -.00000146 00000-0 00000-0 0 9990 +2 41893 0.0231 328.6797 0001923 102.3709 288.9703 1.00273918 1663 +STAR ONE D1 +1 41904U 16082B 17156.52745717 -.00000218 00000-0 00000-0 0 9995 +2 41904 0.0372 91.4069 0002354 350.9388 277.6800 1.00271924 1716 +TJS-2 +1 41911U 17001A 17157.96319302 -.00000345 00000-0 00000-0 0 9992 +2 41911 0.6987 279.6145 0001076 207.3678 222.8143 1.00267197 1667 +HISPASAT 36W-1 +1 41942U 17006A 17156.39406066 -.00000251 00000-0 00000-0 0 9997 +2 41942 0.0574 107.7778 0001097 335.2546 276.9788 1.00272717 1454 +TELKOM 3S +1 41944U 17007A 17156.96517789 -.00000369 00000-0 00000-0 0 9998 +2 41944 0.0196 325.4121 0002604 119.1288 275.4888 1.00269877 1070 +INTELSAT 32E (IS-32E) +1 41945U 17007B 17157.41157112 -.00000274 00000-0 00000-0 0 9995 +2 41945 0.0179 8.1546 0000641 349.1981 2.6473 1.00270850 1176 +ECHOSTAR 23 +1 42070U 17014A 17157.41643175 -.00000278 00000-0 00000-0 0 9996 +2 42070 0.0216 331.8718 0002115 103.7733 284.3784 1.00270214 887 +SES-10 +1 42432U 17017A 17157.47759086 -.00000277 00000-0 00000-0 0 9995 +2 42432 0.0328 338.8066 0002019 110.1376 271.0789 1.00269446 719 +CHINASAT 16 (SJ-13) +1 42662U 17018A 17156.98568600 -.00000359 00000-0 00000-0 0 9997 +2 42662 0.1082 97.0396 0003514 129.1950 133.7356 1.00271270 667 +KOREASAT 7 +1 42691U 17023A 17157.60290750 -.00000367 00000-0 00000-0 0 9992 +2 42691 0.0077 349.1222 0000873 76.8932 162.0996 1.00270811 377 +SGDC +1 42692U 17023B 17157.42566131 -.00000256 00000-0 00000-0 0 9999 +2 42692 0.0526 44.5619 0000855 34.5487 254.8001 1.00271224 375 +GSAT-9 +1 42695U 17024A 17156.71022076 -.00000294 00000-0 00000-0 0 9991 +2 42695 0.0413 178.9945 0003863 260.0872 168.2197 1.00273618 354 \ No newline at end of file diff --git a/data/scene/satellites/tle/gps-ops.txt b/data/scene/satellites/tle/gps-ops.txt new file mode 100644 index 0000000000..75e18e3533 --- /dev/null +++ b/data/scene/satellites/tle/gps-ops.txt @@ -0,0 +1,93 @@ +GPS BIIR-2 (PRN 13) +1 24876U 97035A 17157.59786847 .00000019 00000-0 00000-0 0 9996 +2 24876 55.5513 227.5274 0034589 106.0906 254.3641 2.00561883145810 +GPS BIIR-3 (PRN 11) +1 25933U 99055A 17157.81823278 -.00000081 00000-0 00000-0 0 9990 +2 25933 51.6254 78.0928 0166095 94.4525 354.0300 2.00558378129440 +GPS BIIR-4 (PRN 20) +1 26360U 00025A 17157.82515987 .00000060 00000-0 00000-0 0 9996 +2 26360 53.1170 155.3222 0040993 91.9775 104.3666 2.00561532125131 +GPS BIIR-5 (PRN 28) +1 26407U 00040A 17157.93926722 -.00000076 00000-0 00000-0 0 9998 +2 26407 56.6372 345.5470 0199158 270.1220 289.7792 2.00567008123812 +GPS BIIR-6 (PRN 14) +1 26605U 00071A 17157.87543271 .00000026 00000-0 00000-0 0 9993 +2 26605 55.1227 225.2520 0093903 248.3083 174.0281 2.00565489121414 +GPS BIIR-7 (PRN 18) +1 26690U 01004A 17157.89507387 .00000063 00000-0 00000-0 0 9995 +2 26690 53.0389 158.2602 0183394 256.9116 288.9071 2.00570451119814 +GPS BIIR-8 (PRN 16) +1 27663U 03005A 17157.94951778 -.00000076 00000-0 00000-0 0 9997 +2 27663 56.7061 345.2850 0092504 24.7076 300.8478 2.00560319105174 +GPS BIIR-9 (PRN 21) +1 27704U 03010A 17157.88465515 -.00000050 00000-0 00000-0 0 9993 +2 27704 53.8528 98.6489 0242249 264.5394 334.3334 2.00563816103977 +GPS BIIR-10 (PRN 22) +1 28129U 03058A 17157.28858587 .00000066 00000-0 00000-0 0 9992 +2 28129 52.9268 158.2762 0077488 261.4987 97.6433 2.00547197 98653 +GPS BIIR-11 (PRN 19) +1 28190U 04009A 17157.67197517 -.00000090 00000-0 00000-0 0 9997 +2 28190 55.9984 46.3329 0100885 55.9552 221.8691 2.00564644 96849 +GPS BIIR-12 (PRN 23) +1 28361U 04023A 17157.86272955 .00000033 00000-0 00000-0 0 9990 +2 28361 54.1085 220.3751 0119012 220.1274 106.1343 2.00555923 94908 +GPS BIIR-13 (PRN 02) +1 28474U 04045A 17157.74251056 -.00000050 00000-0 00000-0 0 9995 +2 28474 54.2321 98.1213 0170418 247.2038 351.6925 2.00548722 92298 +GPS BIIRM-1 (PRN 17) +1 28874U 05038A 17157.68738399 -.00000090 00000-0 00000-0 0 9999 +2 28874 56.1546 43.5924 0118451 254.5136 55.9262 2.00568186 85694 +GPS BIIRM-2 (PRN 31) +1 29486U 06042A 17157.89402977 -.00000073 00000-0 00000-0 0 9999 +2 29486 55.4047 282.7005 0084721 343.9526 36.5660 2.00559154 78309 +GPS BIIRM-3 (PRN 12) +1 29601U 06052A 17157.30711204 -.00000075 00000-0 00000-0 0 9993 +2 29601 56.6791 344.2294 0064809 49.4604 311.1238 2.00552294 77266 +GPS BIIRM-4 (PRN 15) +1 32260U 07047A 17157.13563594 .00000032 00000-0 00000-0 0 9993 +2 32260 53.2348 216.7628 0090280 32.1796 328.4497 2.00547487 70695 +GPS BIIRM-5 (PRN 29) +1 32384U 07062A 17156.21822177 -.00000098 00000-0 00000-0 0 9993 +2 32384 56.2180 44.2317 0006335 4.0971 192.7823 2.00570259 69404 +GPS BIIRM-6 (PRN 07) +1 32711U 08012A 17157.53427379 -.00000075 00000-0 00000-0 0 9999 +2 32711 55.1332 282.0856 0107470 212.2063 147.1234 2.00556288 67631 +GPS BIIRM-8 (PRN 05) +1 35752U 09043A 17157.70219478 .00000069 00000-0 00000-0 0 9992 +2 35752 54.2537 159.9422 0048788 28.4210 116.6992 2.00553334 57229 +GPS BIIF-1 (PRN 25) +1 36585U 10022A 17157.89436302 -.00000078 00000-0 00000-0 0 9994 +2 36585 55.9864 341.0322 0063534 45.4099 345.2901 2.00574808 51467 +GPS BIIF-2 (PRN 01) +1 37753U 11036A 17157.85963422 -.00000044 00000-0 00000-0 0 9995 +2 37753 55.4395 101.1871 0066031 30.2070 60.5198 2.00562311 43146 +GPS BIIF-3 (PRN 24) +1 38833U 12053A 17157.72375300 -.00000072 00000-0 00000-0 0 9994 +2 38833 54.1611 279.0056 0055661 25.8719 334.3965 2.00561617 33411 +GPS BIIF-4 (PRN 27) +1 39166U 13023A 17157.07836358 -.00000093 00000-0 00000-0 0 9998 +2 39166 55.8615 40.6350 0047704 18.0209 342.1899 2.00565151 29728 +GPS BIIF-5 (PRN 30) +1 39533U 14008A 17157.58173524 -.00000078 00000-0 00000-0 0 9998 +2 39533 54.3154 284.3055 0028684 182.6529 177.3191 2.00563830 23567 +GPS BIIF-6 (PRN 06) +1 39741U 14026A 17157.35873360 -.00000044 00000-0 00000-0 0 9992 +2 39741 55.4261 100.7272 0009830 291.4939 68.3647 2.00562339 22395 +GPS BIIF-7 (PRN 09) +1 40105U 14045A 17157.45126217 .00000031 00000-0 00000-0 0 9997 +2 40105 54.6318 220.2927 0005069 122.1672 237.9622 2.00569715 19965 +GPS BIIF-8 (PRN 03) +1 40294U 14068A 17157.32446828 .00000074 00000-0 00000-0 0 9997 +2 40294 55.0008 160.9300 0007263 345.6258 14.3805 2.00557500 19060 +GPS BIIF-9 (PRN 26) +1 40534U 15013A 17157.46020623 -.00000081 00000-0 00000-0 0 9990 +2 40534 54.9417 340.1560 0018129 358.6255 1.3832 2.00559260 16117 +GPS BIIF-10 (PRN 08) +1 40730U 15033A 17157.11983367 -.00000094 00000-0 00000-0 0 9999 +2 40730 55.3960 40.3091 0027177 318.4074 41.4282 2.00567662 13865 +GPS BIIF-11 (PRN 10) +1 41019U 15062A 17157.67265313 .00000072 00000-0 00000-0 0 9992 +2 41019 55.0103 160.7038 0026458 206.0557 153.8364 2.00563621 11698 +GPS BIIF-12 (PRN 32) +1 41328U 16007A 17157.76013989 .00000034 00000-0 00000-0 0 9993 +2 41328 54.8890 220.4662 0013935 220.3704 139.6078 2.00556828 9756 diff --git a/data/scene/satellites/tle/stations.txt b/data/scene/satellites/tle/stations.txt new file mode 100644 index 0000000000..fdab931a9d --- /dev/null +++ b/data/scene/satellites/tle/stations.txt @@ -0,0 +1,285 @@ +ISS (ZARYA) +1 25544U 98067A 17157.94655197 .00002630 00000-0 47191-4 0 9994 +2 25544 51.6418 89.3693 0004703 241.3496 157.2990 15.53992000 60168 +TIANGONG 1 +1 37820U 11053A 17157.70967876 .00020914 00000-0 11813-3 0 9994 +2 37820 42.7593 264.1306 0019426 359.5204 148.5490 15.78435713326314 +AGGIESAT 4 +1 41313U 98067HP 17157.16086447 .00018711 00000-0 13577-3 0 9993 +2 41313 51.6375 59.3234 0004723 101.9367 258.2161 15.73442509 77199 +BEVO 2 +1 41314U 98067HQ 17157.88632967 .00703414 31345-3 45291-3 0 9995 +2 41314 51.6177 25.8163 0007485 251.9715 108.0487 16.16778687 77482 +NODES 2 +1 41477U 98067HX 17157.90383726 .00052219 00000-0 26238-3 0 9995 +2 41477 51.6294 56.6595 0003371 94.5932 265.5455 15.81168393 60445 +NODES 1 +1 41478U 98067HY 17157.81578248 .00053127 00000-0 27329-3 0 9992 +2 41478 51.6327 57.5946 0003116 94.0560 266.0798 15.80650524 60427 +FLOCK 2E'-1 +1 41479U 98067HZ 17157.82937824 .00017516 00000-0 15561-3 0 9990 +2 41479 51.6361 69.0720 0001235 132.9379 227.1721 15.68631858 60232 +FLOCK 2E'-3 +1 41480U 98067JA 17157.85350922 .00015886 00000-0 14210-3 0 9991 +2 41480 51.6360 68.5033 0000732 66.0320 294.0753 15.68548906 60237 +FLOCK 2E'-2 +1 41481U 98067JB 17157.93598542 .00014396 00000-0 12524-3 0 9994 +2 41481 51.6345 66.7643 0001590 141.5719 218.5391 15.69326443 60254 +FLOCK 2E'-4 +1 41482U 98067JC 17157.79065223 .00022341 00000-0 17773-3 0 9994 +2 41482 51.6345 66.5289 0001496 79.7829 280.3337 15.71128503 60244 +FLOCK 2E-1 +1 41483U 98067ID 17157.29569450 .00013969 00000-0 13019-3 0 9996 +2 41483 51.6375 72.3135 0002141 32.0954 328.0172 15.67656718 59998 +FLOCK 2E-2 +1 41484U 98067JE 17157.23012824 .00019455 00000-0 16890-3 0 9995 +2 41484 51.6385 71.1428 0002213 46.7114 313.4067 15.69112127 60000 +FLOCK 2E-3 +1 41486U 98067JG 17157.94249624 .00012046 00000-0 11618-3 0 9997 +2 41486 51.6371 70.6259 0002649 64.7641 295.3629 15.66964173 60095 +FLOCK 2E-4 +1 41487U 98067JH 17157.79997702 .00013956 00000-0 12913-3 0 9998 +2 41487 51.6373 69.6496 0003643 57.6827 302.4521 15.67836732 60092 +FLOCK 2E-6 +1 41563U 98067JM 17157.84789515 .00010970 00000-0 10867-3 0 9999 +2 41563 51.6366 71.4681 0001397 138.8569 221.2533 15.66405325 58148 +FLOCK 2E-5 +1 41564U 98067JN 17157.85787038 .00011762 00000-0 11662-3 0 9993 +2 41564 51.6367 71.5423 0001207 135.2529 224.8564 15.66295284 58135 +FLOCK 2E-7 +1 41565U 98067JP 17157.81556449 .00017448 00000-0 15131-3 0 9992 +2 41565 51.6360 68.2287 0004609 80.6839 279.4679 15.69221451 58070 +FLOCK 2E-8 +1 41566U 98067JQ 17157.91086070 .00121698 16725-4 36063-3 0 9997 +2 41566 51.6286 49.8665 0004713 142.6928 217.4407 15.92335896 58310 +FLOCK 2E'-5 +1 41567U 98067JR 17157.78191446 .00011191 00000-0 11495-3 0 9990 +2 41567 51.6378 73.2981 0004907 93.3825 266.7732 15.65457253 57979 +FLOCK 2E'-6 +1 41568U 98067JS 17157.77145550 .00014626 00000-0 13869-3 0 9998 +2 41568 51.6364 70.9405 0004513 92.1048 268.0465 15.67174872 58038 +FLOCK 2E'-8 +1 41569U 98067JT 17157.91417765 .00016441 00000-0 14148-3 0 9993 +2 41569 51.6361 67.3905 0002434 64.2282 295.8966 15.69467197 57974 +FLOCK 2E'-7 +1 41570U 98067JU 17157.81477341 .00022161 00000-0 16934-3 0 9991 +2 41570 51.6364 65.2376 0002923 51.8320 308.2941 15.72094779 58010 +FLOCK 2E-9 +1 41571U 98067JV 17157.85234832 .00017668 00000-0 14924-3 0 9995 +2 41571 51.6366 66.9928 0004309 82.3948 277.7538 15.69854026 57958 +FLOCK 2E-10 +1 41572U 98067JW 17157.81407914 .00017118 00000-0 15232-3 0 9998 +2 41572 51.6364 68.9926 0004709 84.5269 275.6265 15.68604804 57928 +FLOCK 2E-12 +1 41573U 98067JX 17157.94759666 .00016648 00000-0 14342-3 0 9995 +2 41573 51.6359 67.6218 0004148 94.9148 265.2323 15.69425430 57951 +FLOCK 2E-11 +1 41574U 98067JY 17157.94425949 .00017277 00000-0 14780-3 0 9995 +2 41574 51.6347 66.8056 0005101 97.8056 262.3520 15.69560822 57963 +FLOCK 2E'-9 +1 41575U 98067JZ 17157.81983142 .00020007 00000-0 16391-3 0 9991 +2 41575 51.6346 66.7030 0004859 109.2903 250.8620 15.70492303 57943 +FLOCK 2E'-10 +1 41576U 98067KA 17157.81080505 .00018690 00000-0 16006-3 0 9991 +2 41576 51.6368 68.2311 0004669 108.5169 251.6336 15.69470167 57824 +FLOCK 2E'-11 +1 41577U 98067KB 17157.84387094 .00016884 00000-0 14804-3 0 9995 +2 41577 51.6373 68.4600 0005029 90.7988 269.3585 15.68978391 57649 +FLOCK 2E'-12 +1 41578U 98067KC 17157.85229234 .00021128 00000-0 17555-3 0 9991 +2 41578 51.6350 67.7005 0005424 83.6803 276.4812 15.70108031 57654 +FLOCK 2E'-13 +1 41761U 98067KH 17157.84121992 .00007650 00000-0 96912-4 0 9995 +2 41761 51.6386 82.7486 0005694 217.4113 142.6484 15.60558941 41297 +FLOCK 2E'-14 +1 41762U 98067KJ 17157.95581036 .00008251 00000-0 10337-3 0 9991 +2 41762 51.6398 82.0348 0005588 217.7092 142.3510 15.60728191 41318 +FLOCK 2E'-16 +1 41763U 98067KK 17157.86607714 .00009474 00000-0 11591-3 0 9995 +2 41763 51.6398 82.1815 0004711 229.1912 130.8674 15.61149602 41266 +FLOCK 2E'-15 +1 41764U 98067KL 17157.85165937 .00007422 00000-0 94554-4 0 9993 +2 41764 51.6395 82.8349 0005200 225.0806 134.9766 15.60465657 41250 +TIANGONG-2 +1 41765U 16057A 17157.80446253 .00002148 00000-0 29064-4 0 9995 +2 41765 42.7836 56.6308 0011038 266.5003 156.2729 15.61250586 41363 +FLOCK 2E'-18 +1 41769U 98067KM 17157.90167314 .00007746 00000-0 98248-4 0 9998 +2 41769 51.6397 82.4126 0004132 251.5664 108.4880 15.60508848 41269 +FLOCK 2E'-17 +1 41776U 98067KN 17157.95839727 .00007735 00000-0 97973-4 0 9997 +2 41776 51.6407 82.0579 0004483 252.8988 107.1514 15.60548603 41262 +FLOCK 2E'-19 +1 41777U 98067KP 17157.25856684 .00014123 00000-0 15879-3 0 9995 +2 41777 51.6383 83.3506 0003434 249.6465 110.4161 15.62884005 41142 +FLOCK 2E'-20 +1 41782U 98067KQ 17157.95631435 .00009933 00000-0 11651-3 0 9990 +2 41782 51.6397 80.5325 0003339 246.9810 113.0833 15.62193473 41113 +BANXING-2 +1 41834U 16057H 17157.77057558 .00008103 00000-0 75609-4 0 9997 +2 41834 42.7847 52.6919 0007283 184.5004 279.8030 15.67673228 35473 +STARS-C +1 41895U 98067KR 17157.84313134 .00019979 00000-0 23763-3 0 9994 +2 41895 51.6388 85.7683 0002910 282.4174 77.6493 15.61123932 26374 +TANCREDO-1 +1 41931U 98067KT 17157.96072007 .00039094 00000-0 36943-3 0 9993 +2 41931 51.6375 82.7854 0004077 216.5790 143.4928 15.66595418 21991 +ITF-2 +1 41932U 98067KU 17157.88784681 .00010759 00000-0 14197-3 0 9997 +2 41932 51.6410 86.8287 0004551 222.4119 137.6522 15.58968825 21931 +WASEDA-SAT3 +1 41933U 98067KV 17157.86766680 .00012735 00000-0 16372-3 0 9998 +2 41933 51.6393 86.6847 0004401 224.8923 135.1714 15.59470034 21932 +AOBA-VELOX 3 +1 41935U 98067KX 17157.81606468 .00011476 00000-0 14944-3 0 9996 +2 41935 51.6407 87.0963 0003886 226.1035 133.9638 15.59246212 22020 +TUPOD +1 41936U 98067KY 17157.83506049 .00063704 00000-0 50159-3 0 9992 +2 41936 51.6364 81.9421 0002677 193.2921 166.8006 15.70842192 21474 +OSNSAT +1 41939U 98067KZ 17157.84088402 .00022114 00000-0 24411-3 0 9996 +2 41939 51.6393 84.9970 0004937 223.7956 136.2648 15.62981464 21312 +PROGRESS-MS 05 +1 42056U 17010A 17157.62806167 .00002380 00000-0 43406-4 0 9997 +2 42056 51.6419 90.9570 0004682 239.7339 175.9756 15.53988626 60112 +LEMUR-2-REDFERN-GOES +1 42059U 98067LA 17157.87023427 .00010532 00000-0 14537-3 0 9998 +2 42059 51.6398 88.2022 0006952 257.0236 102.9980 15.57773986 14409 +TECHEDSAT 5 +1 42066U 98067LB 17157.88081783 .00130272 00000-0 92629-3 0 9998 +2 42066 51.6337 83.7399 0002362 159.2104 200.8991 15.72955201 14190 +LEMUR-2-TRUTNA +1 42067U 98067LC 17157.83820430 .00021776 00000-0 27852-3 0 9998 +2 42067 51.6394 87.9877 0006160 253.1659 106.8658 15.59134351 14230 +LEMUR-2-AUSTINTACIOUS +1 42068U 98067LD 17157.86892448 .00014740 00000-0 19909-3 0 9997 +2 42068 51.6396 88.1920 0006243 256.1891 103.8407 15.57991820 14338 +LEMUR-2-TRUTNAHD +1 42069U 98067LE 17157.80477585 .00011342 00000-0 15525-3 0 9998 +2 42069 51.6404 88.5168 0006281 253.3949 106.6354 15.57911149 14374 +ISS DEB +1 42434U 98067LF 17157.20389631 .00040897 00000-0 48519-3 0 9998 +2 42434 51.6377 91.2421 0003699 204.1460 155.9360 15.60776006 10458 +CYGNUS OA-7 +1 42681U 17019A 17157.83399044 .00001407 00000-0 30326-4 0 9990 +2 42681 51.6394 89.9608 0004737 238.3552 236.4249 15.52253009 7650 +SOYUZ-MS 04 +1 42682U 17020A 17157.62806167 .00002380 00000-0 43406-4 0 9993 +2 42682 51.6419 90.9570 0004682 239.7339 175.9756 15.53988626 60117 +TIANZHOU 1 +1 42684U 17021A 17153.14877196 .00078565 00000-0 89444-3 0 9998 +2 42684 42.7826 84.4016 0010794 236.6582 266.9899 15.61251018 6668 +ISS DEB +1 42697U 98067LG 17157.84861892 .00037507 00000-0 52116-3 0 9995 +2 42697 51.6412 89.5469 0003850 241.7810 118.2802 15.56620141 5063 +SOMP2 +1 42700U 98067LH 17157.85852898 .00010448 00000-0 15717-3 0 9998 +2 42700 51.6402 89.6186 0005697 289.6047 70.4329 15.55441951 3338 +HAVELSAT +1 42701U 98067LJ 17157.85877506 .00009249 00000-0 14019-3 0 9993 +2 42701 51.6404 89.6204 0005687 290.2093 69.8287 15.55398233 3328 +COLUMBIA +1 42702U 98067LK 17157.85640689 .00019375 00000-0 28110-3 0 9992 +2 42702 51.6402 89.6049 0005582 283.5939 76.4431 15.55829187 3256 +SGSAT +1 42703U 98067LL 17157.85837147 .00016082 00000-0 23650-3 0 9996 +2 42703 51.6399 89.6179 0001779 215.0092 145.0783 15.55619060 3249 +CXBN-2 +1 42704U 98067LM 17157.86087475 .00009857 00000-0 14967-3 0 9992 +2 42704 51.6403 89.6346 0001531 217.8699 142.2185 15.55266596 3259 +ICECUBE +1 42705U 98067LN 17157.86139170 .00012469 00000-0 18766-3 0 9997 +2 42705 51.6404 89.6388 0001477 225.7849 134.3021 15.55229909 3316 +PHOENIX +1 42706U 98067LP 17157.85919097 .00010424 00000-0 15699-3 0 9992 +2 42706 51.6405 89.6243 0000606 264.1635 95.9287 15.55419085 3245 +X-CUBESAT +1 42707U 98067LQ 17157.85942325 .00010489 00000-0 15798-3 0 9994 +2 42707 51.6402 89.6253 0000714 261.3522 98.7388 15.55408199 3234 +QBEE50-LTU-OC +1 42708U 98067LR 17157.85976115 .00010073 00000-0 15218-3 0 9995 +2 42708 51.6403 89.6273 0000806 257.2200 102.8701 15.55374747 3199 +ALTAIR PATHFINDER +1 42711U 98067LS 17157.86492244 .00006899 00000-0 10825-3 0 9996 +2 42711 51.6401 89.6607 0004088 214.3267 145.7460 15.54917330 2901 +SHARC +1 42712U 98067LT 17157.86348986 .00006919 00000-0 10815-3 0 9992 +2 42712 51.6404 89.6521 0001998 212.8804 147.2063 15.55020563 3144 +ZA-AEROSAT +1 42713U 98067LU 17157.86235707 .00008829 00000-0 13523-3 0 9990 +2 42713 51.6402 89.6446 0001922 199.0347 161.0573 15.55180619 2906 +LINK +1 42714U 98067LV 17157.86266135 .00009504 00000-0 14497-3 0 9992 +2 42714 51.6401 89.6466 0002117 206.7176 153.3706 15.55190802 3011 +CSUNSAT 1 +1 42715U 98067LW 17157.86415953 .00009161 00000-0 14070-3 0 9993 +2 42715 51.6400 89.6561 0004236 203.1814 156.8987 15.55050192 2904 +UPSAT +1 42716U 98067LX 17157.86094233 .00011468 00000-0 17226-3 0 9997 +2 42716 51.6402 89.6352 0004008 295.0779 64.9796 15.55373568 3039 +SPACECUBE +1 42717U 98067LY 17157.86170034 .00009917 00000-0 15049-3 0 9999 +2 42717 51.6403 89.6401 0003993 293.5006 66.5565 15.55272760 2273 +HOOPOE +1 42718U 98067LZ 17157.86153836 .00010781 00000-0 16274-3 0 9993 +2 42718 51.6403 89.6391 0003987 292.5690 67.4879 15.55311723 2274 +UNSW-ECO +1 42721U 98067MA 17157.80343503 .00013771 00000-0 20735-3 0 9992 +2 42721 51.6403 89.9999 0007120 268.2498 91.7677 15.55097441 1963 +NJUST-1 +1 42722U 98067MB 17157.25292095 .00001440 00000-0 28291-4 0 9997 +2 42722 51.6402 92.7556 0007027 267.2164 245.0421 15.54901758 1874 +CHALLENGER +1 42723U 98067MC 17157.80404552 .00011009 00000-0 16798-3 0 9992 +2 42723 51.6404 90.0040 0006958 268.5782 91.4412 15.54978044 1941 +DUTHSAT +1 42724U 98067MD 17157.80428604 .00013264 00000-0 20078-3 0 9998 +2 42724 51.6404 90.0056 0006075 271.4432 88.5863 15.54994883 1861 +LILACSAT-1 +1 42725U 98067ME 17157.80476334 .00008530 00000-0 13233-3 0 9997 +2 42725 51.6404 90.0092 0006008 272.8653 87.1650 15.54873181 1864 +NSIGHT-1 +1 42726U 98067MF 17157.86942714 .00006534 00000-0 10336-3 0 9997 +2 42726 51.6405 89.6907 0005990 273.7292 86.3014 15.54792750 1882 +SNUSAT-1 +1 42727U 98067MG 17157.86945727 .00009989 00000-0 15371-3 0 9996 +2 42727 51.6398 89.6902 0007575 228.4973 131.5369 15.54864564 1848 +QBITO +1 42728U 98067MH 17155.94045484 .00010441 00000-0 16007-3 0 9997 +2 42728 51.6397 99.3085 0003029 277.2248 82.8398 15.54926471 1380 +AALTO-2 +1 42729U 98067MJ 17155.81189444 .00010269 00000-0 15754-3 0 9995 +2 42729 51.6399 99.9497 0003050 275.8476 84.2168 15.54929551 1379 +SUSAT +1 42730U 98067MK 17157.80448090 .00010291 00000-0 15784-3 0 9991 +2 42730 51.6404 90.0078 0003110 281.5326 78.5315 15.54933074 1678 +I-INSPIRE II +1 42731U 98067ML 17156.13327249 .00009265 00000-0 14256-3 0 9991 +2 42731 51.6393 98.3448 0007164 207.3399 152.7216 15.54972835 1467 +POLYITAN-2-SAU +1 42732U 98067MM 17155.94043987 .00010678 00000-0 16312-3 0 9999 +2 42732 51.6392 99.3069 0007216 206.4771 153.5852 15.54981238 1438 +SNUSAT-1B +1 42733U 98067MN 17157.80452231 .00009513 00000-0 14625-3 0 9994 +2 42733 51.6398 90.0067 0007034 214.9107 145.1423 15.54959154 1721 +EXALTA-1 +1 42734U 98067MP 17156.39039283 .00009453 00000-0 14516-3 0 9998 +2 42734 51.6396 97.0623 0008589 235.9084 124.1092 15.54988337 1452 +AOXIANG-1 +1 42735U 98067MQ 17157.80450021 .00008356 00000-0 12927-3 0 9998 +2 42735 51.6402 90.0072 0007677 265.5134 94.4980 15.54971752 3094 +BEEAGLESAT +1 42736U 98067MR 17155.94091570 .00008525 00000-0 13203-3 0 9998 +2 42736 51.6398 99.3106 0007570 257.5066 102.5078 15.54911642 1499 +ATLANTIS +1 42737U 98067MS 17157.73973140 .00018727 00000-0 27851-3 0 9999 +2 42737 51.6395 90.3231 0007506 257.5958 102.4193 15.55165668 1662 +DRAGON CRS-11 +1 42744U 17030A 17157.37842914 .00002356 00000-0 43046-4 0 9998 +2 42744 51.6419 92.2016 0004681 238.9005 219.3491 15.53987082 60075 +FALCON 9 DEB +1 42745U 17030B 17156.27917736 -.00003619 11440-4 00000-0 0 9999 +2 42745 51.6486 97.5083 0103286 51.0958 103.4391 16.01768424 228 +FALCON 9 DEB +1 42746U 17030C 17156.87548135 -.00561144 11417-4 -52194-3 0 9990 +2 42746 51.6257 94.2528 0099282 55.6445 305.5405 16.01548785 322 diff --git a/data/scene/satellites/verify_satellites_lua_output.lua b/data/scene/satellites/verify_satellites_lua_output.lua new file mode 100644 index 0000000000..224ce53cc0 --- /dev/null +++ b/data/scene/satellites/verify_satellites_lua_output.lua @@ -0,0 +1,48 @@ +#!/bin/lua + +function tableLength(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end + +output = dofile("satellites.mod") +outputLen = tableLength(output) + +for i=1,outputLen do + type=output[i].Renderable.Type + print("------------", i) + if( type == "RenderablePlane" ) then + print(output[i].Name) + print(output[i].Parent) + print(output[i].Renderable.Type) + print(output[i].Renderable.Size[1]) + print(output[i].Renderable.Size[2]) + print(output[i].Renderable.Origin) + print(output[i].Renderable.Body) + print(output[i].Renderable.Billboard) + print(output[i].Renderable.Texture) + print(output[i].Transform.Translation.Type) + print(output[i].Transform.Translation.Body) + print(output[i].Transform.Translation.Observer) + print(output[i].Transform.Translation.File) + print(output[i].Transform.Translation.LineNum) + print(output[i].Transform.Scale.Type) + print(output[i].Transform.Scale.Scale) + elseif( type == "RenderableTrailOrbit" ) then + print(output[i].Name) + print(output[i].Parent) + print(output[i].Renderable.Type) + print(output[i].Renderable.Translation.Type) + print(output[i].Renderable.Translation.Body) + print(output[i].Renderable.Translation.Observer) + print(output[i].Renderable.Translation.File) + print(output[i].Renderable.Translation.LineNum) + print(output[i].Renderable.Color[1]) + print(output[i].Renderable.Color[2]) + print(output[i].Renderable.Color[3]) + print(output[i].Renderable.Period) + print(output[i].Renderable.Resolution) + print(output[i].GuiName) + end +end diff --git a/ext/ghoul b/ext/ghoul index a8489bba6c..b362a77feb 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit a8489bba6c31204140cb8021970e3ff141da6c2e +Subproject commit b362a77feb81c751519386d42615ba04e0c92d1c diff --git a/ext/json/json.hpp b/ext/json/json.hpp index db73336723..6dfc1831fa 100644 --- a/ext/json/json.hpp +++ b/ext/json/json.hpp @@ -1,70 +1,70 @@ -/*! -@mainpage +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 2.1.1 +|_____|_____|_____|_|___| https://github.com/nlohmann/json -These pages contain the API documentation of JSON for Modern C++, a C++11 -header-only JSON class. +Licensed under the MIT License . +Copyright (c) 2013-2017 Niels Lohmann . -Class @ref nlohmann::basic_json is a good entry point for the documentation. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -@copyright The code is licensed under the [MIT - License](http://opensource.org/licenses/MIT): -
- Copyright © 2013-2016 Niels Lohmann. -
- Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: -
- The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. -
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -@author [Niels Lohmann](http://nlohmann.me) -@see https://github.com/nlohmann/json to download the source code - -@version 1.1.0 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ #ifndef NLOHMANN_JSON_HPP #define NLOHMANN_JSON_HPP -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include // all_of, copy, fill, find, for_each, none_of, remove, reverse, transform +#include // array +#include // assert +#include // isdigit +#include // and, not, or +#include // isfinite, labs, ldexp, signbit +#include // nullptr_t, ptrdiff_t, size_t +#include // int64_t, uint64_t +#include // abort, strtod, strtof, strtold, strtoul, strtoll, strtoull +#include // strlen +#include // forward_list +#include // function, hash, less +#include // initializer_list +#include // setw +#include // istream, ostream +#include // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator +#include // numeric_limits +#include // locale +#include // map +#include // addressof, allocator, allocator_traits, unique_ptr +#include // accumulate +#include // stringstream +#include // domain_error, invalid_argument, out_of_range +#include // getline, stoi, string, to_string +#include // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type +#include // declval, forward, make_pair, move, pair, swap +#include // vector -// enable ssize_t on MinGW -#ifdef __GNUC__ - #ifdef __MINGW32__ - #include +// exclude unsupported compilers +#if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif +#elif defined(__GNUC__) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" #endif #endif @@ -74,10 +74,30 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation. #pragma GCC diagnostic ignored "-Wfloat-equal" #endif -// enable ssize_t for MSVC -#ifdef _MSC_VER - #include - using ssize_t = SSIZE_T; +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + +// allow to disable exceptions +#if not defined(JSON_NOEXCEPTION) || defined(__EXCEPTIONS) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) +#else + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) #endif /*! @@ -88,51 +108,869 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation. namespace nlohmann { - /*! @brief unnamed namespace with internal helper functions + +This namespace collects some functions that could not be defined inside the +@ref basic_json class. + +@since version 2.1.0 +*/ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + @since version 1.0.0 */ -namespace +enum class value_t : uint8_t { -/*! -@brief Helper to determine whether there's a key_type for T. -@sa http://stackoverflow.com/a/7728728/266378 -*/ -template -struct has_mapped_type -{ - private: - template static char test(typename C::mapped_type*); - template static char (&test(...))[2]; - public: - static constexpr bool value = sizeof(test(0)) == 1; + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + discarded ///< discarded by the the parser callback function }; +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string +- furthermore, each type is not smaller than itself + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0, // null + 3, // object + 4, // array + 5, // string + 1, // boolean + 2, // integer + 2, // unsigned + 2, // float + } + }; + + // discarded values are not comparable + if (lhs == value_t::discarded or rhs == value_t::discarded) + { + return false; + } + + return order[static_cast(lhs)] < + order[static_cast(rhs)]; } + +///////////// +// helpers // +///////////// + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// taken from http://stackoverflow.com/a/26936864/266378 +template +using is_unscoped_enum = + std::integral_constant::value and + std::is_enum::value>; + +/* +Implementation of two C++17 constructs: conjunction, negation. This is needed +to avoid evaluating all the traits in a condition + +For example: not std::is_same::value and has_value_type::value +will not compile when T = void (on MSVC at least). Whereas +conjunction>, has_value_type>::value will +stop evaluating if negation<...>::value == false + +Please note that those constructs must be used with caution, since symbols can +become very long quickly (which can slow down compilation and cause MSVC +internal compiler errors). Only use it when you have to (see example ahead). +*/ +template struct conjunction : std::true_type {}; +template struct conjunction : B1 {}; +template +struct conjunction : std::conditional, B1>::type {}; + +template struct negation : std::integral_constant < bool, !B::value > {}; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + + +////////////////// +// constructors // +////////////////// + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + // replace infinity and NAN by null + if (not std::isfinite(val)) + { + j = BasicJsonType{}; + } + else + { + j.m_type = value_t::number_float; + j.m_value = val; + } + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } +}; + + +//////////////////////// +// has_/is_ functions // +//////////////////////// + +/*! +@brief Helper to determine whether there's a key_type for T. + +This helper is used to tell associative containers apart from other containers +such as sequence containers. For instance, `std::map` passes the test as it +contains a `mapped_type`, whereas `std::vector` fails the test. + +@sa http://stackoverflow.com/a/7728728/266378 +@since version 1.0.0, overworked in version 2.0.6 +*/ +#define NLOHMANN_JSON_HAS_HELPER(type) \ + template struct has_##type { \ + private: \ + template \ + static int detect(U &&); \ + static void detect(...); \ + public: \ + static constexpr bool value = \ + std::is_integral()))>::value; \ + } + +NLOHMANN_JSON_HAS_HELPER(mapped_type); +NLOHMANN_JSON_HAS_HELPER(key_type); +NLOHMANN_JSON_HAS_HELPER(value_type); +NLOHMANN_JSON_HAS_HELPER(iterator); + +#undef NLOHMANN_JSON_HAS_HELPER + + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl +{ + static constexpr auto value = + std::is_constructible::value and + std::is_constructible::value; +}; + +template +struct is_compatible_object_type +{ + static auto constexpr value = is_compatible_object_type_impl < + conjunction>, + has_mapped_type, + has_key_type>::value, + typename BasicJsonType::object_t, CompatibleObjectType >::value; +}; + +template +struct is_basic_json_nested_type +{ + static auto constexpr value = std::is_same::value or + std::is_same::value or + std::is_same::value or + std::is_same::value or + std::is_same::value; +}; + +template +struct is_compatible_array_type +{ + static auto constexpr value = + conjunction>, + negation>, + negation>, + negation>, + has_value_type, + has_iterator>::value; +}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + std::is_constructible::value and + CompatibleLimits::is_integer and + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type +{ + static constexpr auto value = + is_compatible_integer_type_impl < + std::is_integral::value and + not std::is_same::value, + RealIntegerType, CompatibleNumberIntegerType > ::value; +}; + + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json +{ + private: + // also check the return type of from_json + template::from_json( + std::declval(), std::declval()))>::value>> + static int detect(U&&); + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json +{ + private: + template < + typename U, + typename = enable_if_t::from_json(std::declval()))>::value >> + static int detect(U&&); + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +template +struct has_to_json +{ + private: + template::to_json( + std::declval(), std::declval()))> + static int detect(U&&); + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; +}; + + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template < + typename BasicJsonType, typename CompatibleNumberUnsignedType, + enable_if_t::value, int> = 0 > +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template < + typename BasicJsonType, typename CompatibleNumberIntegerType, + enable_if_t::value, int> = 0 > +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, UnscopedEnumType e) noexcept +{ + external_constructor::construct(j, e); +} + +template < + typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < + is_compatible_array_type::value or + std::is_same::value, + int > = 0 > +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template < + typename BasicJsonType, typename CompatibleObjectType, + enable_if_t::value, + int> = 0 > +void to_json(BasicJsonType& j, const CompatibleObjectType& arr) +{ + external_constructor::construct(j, arr); +} + + +/////////////// +// from_json // +/////////////// + +// overloads for basic_json template parameters +template::value and + not std::is_same::value, + int> = 0> +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast( + *j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast( + *j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast( + *j.template get_ptr()); + break; + } + default: + { + JSON_THROW( + std::domain_error("type must be number, but is " + j.type_name())); + } + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (not j.is_boolean()) + { + JSON_THROW(std::domain_error("type must be boolean, but is " + j.type_name())); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (not j.is_string()) + { + JSON_THROW(std::domain_error("type must be string, but is " + j.type_name())); + } + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, UnscopedEnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) +{ + if (not j.is_array()) + { + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + } + arr = *j.template get_ptr(); +} + +// forward_list doesn't have an insert method +template +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + // do not perform the check when user wants to retrieve jsons + // (except when it's null.. ?) + if (j.is_null()) + { + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + } + if (not std::is_same::value) + { + if (not j.is_array()) + { + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + } + } + for (auto it = j.rbegin(), end = j.rend(); it != end; ++it) + { + l.push_front(it->template get()); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0>) +{ + using std::begin; + using std::end; + + std::transform(j.begin(), j.end(), + std::inserter(arr, end(arr)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); +} + +template +auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1>) +-> decltype( + arr.reserve(std::declval()), + void()) +{ + using std::begin; + using std::end; + + arr.reserve(j.size()); + std::transform( + j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); +} + +template::value and + not std::is_same::value, int> = 0> +void from_json(const BasicJsonType& j, CompatibleArrayType& arr) +{ + if (j.is_null()) + { + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + } + + // when T == BasicJsonType, do not check if value_t is correct + if (not std::is_same::value) + { + if (not j.is_array()) + { + JSON_THROW(std::domain_error("type must be array, but is " + j.type_name())); + } + } + from_json_array_impl(j, arr, priority_tag<1> {}); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, CompatibleObjectType& obj) +{ + if (not j.is_object()) + { + JSON_THROW(std::domain_error("type must be object, but is " + j.type_name())); + } + + auto inner_object = j.template get_ptr(); + using std::begin; + using std::end; + // we could avoid the assignment, but this might require a for loop, which + // might be less efficient than the container constructor for some + // containers (would it?) + obj = CompatibleObjectType(begin(*inner_object), end(*inner_object)); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int> = 0> +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + default: + { + JSON_THROW(std::domain_error("type must be number, but is " + j.type_name())); + } + } +} + +struct to_json_fn +{ + private: + template + auto call(BasicJsonType& j, T&& val, priority_tag<1>) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } + + template + void call(BasicJsonType&, T&&, priority_tag<0>) const noexcept + { + static_assert(sizeof(BasicJsonType) == 0, + "could not find to_json() method in T's namespace"); + } + + public: + template + void operator()(BasicJsonType& j, T&& val) const + noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1> {}))) + { + return call(j, std::forward(val), priority_tag<1> {}); + } +}; + +struct from_json_fn +{ + private: + template + auto call(const BasicJsonType& j, T& val, priority_tag<1>) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } + + template + void call(const BasicJsonType&, T&, priority_tag<0>) const noexcept + { + static_assert(sizeof(BasicJsonType) == 0, + "could not find from_json() method in T's namespace"); + } + + public: + template + void operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(std::declval().call(j, val, priority_tag<1> {}))) + { + return call(j, val, priority_tag<1> {}); + } +}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail + + +/// namespace to hold default `to_json` / `from_json` functions +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +constexpr const auto& from_json = detail::static_const::value; +} + + +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static void from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static void to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; + + /*! @brief a class to store JSON values -@tparam ObjectType type for JSON objects (@c std::map by default; will be used +@tparam ObjectType type for JSON objects (`std::map` by default; will be used in @ref object_t) -@tparam ArrayType type for JSON arrays (@c std::vector by default; will be used +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used in @ref array_t) -@tparam StringType type for JSON strings and object keys (@c std::string by +@tparam StringType type for JSON strings and object keys (`std::string` by default; will be used in @ref string_t) -@tparam BooleanType type for JSON booleans (@c `bool` by default; will be used +@tparam BooleanType type for JSON booleans (`bool` by default; will be used in @ref boolean_t) -@tparam NumberIntegerType type for JSON integer numbers (@c `int64_t` by +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by default; will be used in @ref number_integer_t) -@tparam NumberFloatType type for JSON floating-point numbers (@c `double` by +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by default; will be used in @ref number_float_t) -@tparam AllocatorType type of the allocator to use (@c `std::allocator` by +@tparam AllocatorType type of the allocator to use (`std::allocator` by default) +@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` +and `from_json()` (@ref adl_serializer by default) @requirement The class satisfies the following concept requirements: - Basic - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null value. + JSON values can be default constructed. The result will be a JSON null + value. - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): A JSON value can be constructed from an rvalue argument. - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): @@ -147,8 +985,8 @@ default) - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): JSON values have [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the class - has no virtual functions or (virtual) base classes. + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. - Library-wide - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): JSON values can be compared with `==`, see @ref @@ -169,11 +1007,19 @@ default) JSON values can be used like STL containers and provide reverse iterator access. +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + @internal @note ObjectType trick from http://stackoverflow.com/a/9860911 @endinternal -@see RFC 7159 +@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange +Format](http://rfc7159.net/rfc7159) @since version 1.0.0 @@ -184,29 +1030,37 @@ template < template class ArrayType = std::vector, class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = int64_t, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, - template class AllocatorType = std::allocator + template class AllocatorType = std::allocator, + template class JSONSerializer = adl_serializer > class basic_json { private: + template friend struct detail::external_constructor; /// workaround type for MSVC - using basic_json_t = basic_json; + using basic_json_t = basic_json; public: + using value_t = detail::value_t; + // forward declarations + template class iter_impl; + template class json_reverse_iterator; + class json_pointer; + template + using json_serializer = JSONSerializer; ///////////////////// // container types // ///////////////////// /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. /// @{ /// the type of elements in a basic_json container @@ -230,13 +1084,10 @@ class basic_json /// the type of an element const pointer using const_pointer = typename std::allocator_traits::const_pointer; - // forward declaration - template class json_reverse_iterator; - /// an iterator for a basic_json container - class iterator; + using iterator = iter_impl; /// a const iterator for a basic_json container - class const_iterator; + using const_iterator = iter_impl; /// a reverse iterator for a basic_json container using reverse_iterator = json_reverse_iterator; /// a const reverse iterator for a basic_json container @@ -253,12 +1104,92 @@ class basic_json return allocator_type(); } + /*! + @brief returns version information on the library + + This function returns a JSON object with information about the library, + including the version number and information on the platform and compiler. + + @return JSON object holding version information + key | description + ----------- | --------------- + `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). + `copyright` | The copyright line for the library as string. + `name` | The name of the library as string. + `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. + `url` | The URL of the project as string. + `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + + @liveexample{The following code shows an example output of the `meta()` + function.,meta} + + @complexity Constant. + + @since 2.1.0 + */ + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"] = + { + {"string", "2.1.1"}, + {"major", 2}, + {"minor", 1}, + {"patch", 1} + }; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + /////////////////////////// // JSON value data types // /////////////////////////// /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. /// @{ /*! @@ -274,8 +1205,8 @@ class basic_json @tparam ObjectType the container to store objects (e.g., `std::map` or `std::unordered_map`) - @tparam StringType the type of the keys or names (e.g., `std::string`). The - comparison function `std::less` is used to order elements + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements inside the container. @tparam AllocatorType the allocator to use for objects (e.g., `std::allocator`) @@ -283,8 +1214,8 @@ class basic_json #### Default type With the default values for @a ObjectType (`std::map`), @a StringType - (`std::string`), and @a AllocatorType (`std::allocator`), the default value - for @a object_t is: + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: @code {.cpp} std::map< @@ -301,16 +1232,16 @@ class basic_json the default type, objects have the following behavior: - When all names are unique, objects will be interoperable in the sense - that all software implementations receiving that object will agree on the - name-value mappings. + that all software implementations receiving that object will agree on + the name-value mappings. - When the names within an object are not unique, later stored name/value pairs overwrite previously stored name/value pairs, leaving the used names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will be treated as equal and both stored as `{"key": 1}`. - Internally, name/value pairs are stored in lexicographical order of the - names. Objects will also be serialized (see @ref dump) in this order. For - instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored and - serialized as `{"a": 2, "b": 1}`. + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. - When comparing objects, the order of the name/value pairs is irrelevant. This makes objects interoperable in the sense that they will not be affected by these differences. For instance, `{"b": 1, "a": 2}` and @@ -323,23 +1254,24 @@ class basic_json In this class, the object's limit of nesting is not constraint explicitly. However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the @ref - max_size function of a JSON object. + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. #### Storage Objects are stored as pointers in a @ref basic_json type. That is, for any - access to object values, a pointer of type `object_t*` must be dereferenced. + access to object values, a pointer of type `object_t*` must be + dereferenced. @sa @ref array_t -- type for an array value @since version 1.0.0 - @note The order name/value pairs are added to the object is *not* preserved - by the library. Therefore, iterating an object may return name/value pairs - in a different order than they were originally stored. In fact, keys will - be traversed in alphabetical order as `std::map` with `std::less` is used - by default. Please note this behavior conforms to [RFC + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC 7159](http://rfc7159.net/rfc7159), because any order implements the specified "unordered" nature of JSON objects. */ @@ -360,7 +1292,7 @@ class basic_json @tparam ArrayType container type to store arrays (e.g., `std::vector` or `std::list`) - @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) #### Default type @@ -381,8 +1313,8 @@ class basic_json In this class, the array's limit of nesting is not constraint explicitly. However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the @ref - max_size function of a JSON array. + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. #### Storage @@ -402,8 +1334,8 @@ class basic_json > A string is a sequence of zero or more Unicode characters. To store objects in C++, a type is defined by the template parameter - described below. Unicode values are split by the JSON class into byte-sized - characters during deserialization. + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. @tparam StringType the container to store strings (e.g., `std::string`). Note this container is used for keys/names in objects, see @ref object_t. @@ -417,6 +1349,12 @@ class basic_json std::string @endcode + #### Encoding + + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. + #### String comparison [RFC 7159](http://rfc7159.net/rfc7159) states: @@ -472,18 +1410,19 @@ class basic_json @brief a type for a number (integer) [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most programming - > languages. A number is represented in base 10 using decimal digits. It - > contains an integer component that may be prefixed with an optional minus - > sign, which may be followed by a fraction part and/or an exponent part. - > Leading zeros are not allowed. (...) Numeric values that cannot be - > represented in the grammar below (such as Infinity and NaN) are not - > permitted. + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. - This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. To store integer numbers in C++, a type is defined by the template parameter @a NumberIntegerType which chooses the type to use. @@ -502,8 +1441,8 @@ class basic_json - The restrictions about leading zeros is not enforced in C++. Instead, leading zeros in integer literals lead to an interpretation as octal number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. During - deserialization, leading zeros yield an error. + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. #### Limits @@ -514,9 +1453,10 @@ class basic_json When the default type is used, the maximal integer number that can be stored is `9223372036854775807` (INT64_MAX) and the minimal integer number that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers - that are out of range will yield over/underflow when used in a constructor. - During deserialization, too large or small integer numbers will be - automatically be stored as @ref number_float_t. + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. [RFC 7159](http://rfc7159.net/rfc7159) further states: > Note that when such software is used, numbers that are integers and are @@ -532,26 +1472,100 @@ class basic_json @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + @since version 1.0.0 */ using number_integer_t = NumberIntegerType; + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + /*! @brief a type for a number (floating-point) [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most programming - > languages. A number is represented in base 10 using decimal digits. It - > contains an integer component that may be prefixed with an optional minus - > sign, which may be followed by a fraction part and/or an exponent part. - > Leading zeros are not allowed. (...) Numeric values that cannot be - > represented in the grammar below (such as Infinity and NaN) are not - > permitted. + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. - This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. To store floating-point numbers in C++, a type is defined by the template parameter @a NumberFloatType which chooses the type to use. @@ -568,8 +1582,8 @@ class basic_json #### Default behavior - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in floating-point literals will be ignored. Internally, the - value will be stored as decimal number. For instance, the C++ + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ floating-point literal `01.2` will be serialized to `1.2`. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to `null`. @@ -580,9 +1594,9 @@ class basic_json > This specification allows implementations to set limits on the range and > precision of numbers accepted. Since software that implements IEEE > 754-2008 binary64 (double precision) numbers is generally available and - > widely used, good interoperability can be achieved by implementations that - > expect no more precision or range than these provide, in the sense that - > implementations will approximate JSON numbers within the expected + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected > precision. This implementation does exactly follow this approach, as it uses double @@ -597,41 +1611,16 @@ class basic_json @sa @ref number_integer_t -- type for number values (integer) + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + @since version 1.0.0 */ using number_float_t = NumberFloatType; /// @} - - /////////////////////////// - // JSON type enumeration // - /////////////////////////// - - /*! - @brief the JSON type enumeration - - This enumeration collects the different JSON types. It is internally used - to distinguish the stored values, and the functions @ref is_null(), @ref - is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref - is_number(), and @ref is_discarded() rely on it. - - @since version 1.0.0 - */ - enum class value_t : uint8_t - { - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (integer) - number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function - }; - - private: + /// helper for exception-safe object creation template static T* create(Args&& ... args) @@ -643,6 +1632,7 @@ class basic_json }; std::unique_ptr object(alloc.allocate(1), deleter); alloc.construct(object.get(), std::forward(args)...); + assert(object != nullptr); return object.release(); } @@ -653,7 +1643,24 @@ class basic_json /*! @brief a JSON value - The actual storage for a JSON value of the @ref basic_json class. + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. @since version 1.0.0 */ @@ -669,15 +1676,19 @@ class basic_json boolean_t boolean; /// number (integer) number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; /// number (floating-point) number_float_t number_float; /// default constructor (for null values) - json_value() noexcept = default; + json_value() = default; /// constructor for booleans json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} /// constructor for numbers (floating-point) json_value(number_float_t v) noexcept : number_float(v) {} /// constructor for empty values of a given type @@ -715,14 +1726,29 @@ class basic_json break; } + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } + case value_t::number_float: { number_float = number_float_t(0.0); break; } + case value_t::null: + { + break; + } + default: { + if (t == value_t::null) + { + JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE + } break; } } @@ -747,6 +1773,21 @@ class basic_json } }; + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const + { + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); + } public: ////////////////////////// @@ -759,6 +1800,8 @@ class basic_json This enumeration lists the parser events that can trigger calling a callback function of type @ref parser_callback_t during parsing. + @image html callback_events.png "Example when certain parse events are triggered" + @since version 1.0.0 */ enum class parse_event_t : uint8_t @@ -781,12 +1824,13 @@ class basic_json @brief per-element parser callback type With a parser callback function, the result of parsing a JSON text can be - influenced. When passed to @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t), it is called on certain - events (passed as @ref parse_event_t via parameter @a event) with a set - recursion depth @a depth and context JSON value @a parsed. The return value - of the callback function is a boolean indicating whether the element that - emitted the callback shall be kept or not. + influenced. When passed to @ref parse(std::istream&, const + parser_callback_t) or @ref parse(const CharT, const parser_callback_t), + it is called on certain events (passed as @ref parse_event_t via parameter + @a event) with a set recursion depth @a depth and context JSON value + @a parsed. The return value of the callback function is a boolean + indicating whether the element that emitted the callback shall be kept or + not. We distinguish six scenarios (determined by the event type) in which the callback function can be called. The following table describes the values @@ -801,13 +1845,15 @@ class basic_json parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + @image html callback_events.png "Example when certain parse events are triggered" + Discarding a value (i.e., returning `false`) has different effects depending on the context in which function was called: - Discarded values in structured types are skipped. That is, the parser will behave as if the discarded value was never read. - - In case a value outside a structured type is skipped, it is replaced with - `null`. This case happens if the top-level element is skipped. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. @param[in] depth the depth of the recursion during parsing @@ -822,11 +1868,13 @@ class basic_json skipped completely or replaced by an empty discarded object. @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t) for examples + @ref parse(const CharT, const parser_callback_t) for examples @since version 1.0.0 */ - using parser_callback_t = std::function; + using parser_callback_t = std::function; ////////////////// @@ -834,6 +1882,8 @@ class basic_json ////////////////// /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. /// @{ /*! @@ -861,460 +1911,105 @@ class basic_json @liveexample{The following code shows the constructor for different @ref value_t values,basic_json__value_t} - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - @sa @ref basic_json(boolean_t value) -- create a boolean value - @sa @ref basic_json(const string_t&) -- create a string value - @sa @ref basic_json(const object_t&) -- create a object value - @sa @ref basic_json(const array_t&) -- create a array value - @sa @ref basic_json(const number_float_t) -- create a number - (floating-point) value - @sa @ref basic_json(const number_integer_t) -- create a number (integer) - value - @since version 1.0.0 */ basic_json(const value_t value_type) : m_type(value_type), m_value(value_type) - {} + { + assert_invariant(); + } /*! - @brief create a null object (implicitly) + @brief create a null object - Create a `null` JSON value. This is the implicit version of the `null` - value constructor as it takes no parameters. + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. @complexity Constant. - @requirement This function satisfies the Container requirements: - - The complexity is constant. - - As postcondition, it holds: `basic_json().empty() == true`. + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. - @liveexample{The following code shows the constructor for a `null` JSON - value.,basic_json} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ - basic_json() noexcept = default; - - /*! - @brief create a null object (explicitly) - - Create a `null` JSON value. This is the explicitly version of the `null` - value constructor as it takes a null pointer as parameter. It allows to - create `null` values by explicitly assigning a @c nullptr to a JSON value. - The passed null pointer itself is not read -- it is only used to choose the - right constructor. - - @complexity Constant. - - @liveexample{The following code shows the constructor with null pointer - parameter.,basic_json__nullptr_t} - - @sa @ref basic_json() -- default constructor (implicitly creating a `null` - value) - - @since version 1.0.0 - */ - basic_json(std::nullptr_t) noexcept + basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) - {} + { + assert_invariant(); + } /*! - @brief create an object (explicit) + @brief create a JSON value - Create an object JSON value with a given content. + This is a "catch all" constructor for all compatible JSON types; that is, + types for which a `to_json()` method exsits. The constructor forwards the + parameter @a val to that method (to `json_serializer::to_json` method + with `U = uncvref_t`, to be exact). - @param[in] val a value for the object + Template type @a CompatibleType includes, but is not limited to, the + following types: + - **arrays**: @ref array_t and all kinds of compatible containers such as + `std::vector`, `std::deque`, `std::list`, `std::forward_list`, + `std::array`, `std::set`, `std::unordered_set`, `std::multiset`, and + `unordered_multiset` with a `value_type` from which a @ref basic_json + value can be constructed. + - **objects**: @ref object_t and all kinds of compatible associative + containers such as `std::map`, `std::unordered_map`, `std::multimap`, + and `std::unordered_multimap` with a `key_type` compatible to + @ref string_t and a `value_type` from which a @ref basic_json value can + be constructed. + - **strings**: @ref string_t, string literals, and all compatible string + containers can be used. + - **numbers**: @ref number_integer_t, @ref number_unsigned_t, + @ref number_float_t, and all convertible number types such as `int`, + `size_t`, `int64_t`, `float` or `double` can be used. + - **boolean**: @ref boolean_t / `bool` can be used. - @complexity Linear in the size of the passed @a val. + See the examples below. - @throw std::bad_alloc if allocation for object value fails + @tparam CompatibleType a type such that: + - @a CompatibleType is not derived from `std::istream`, + - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move + constructors), + - @a CompatibleType is not a @ref basic_json nested type (e.g., + @ref json_pointer, @ref iterator, etc ...) + - @ref @ref json_serializer has a + `to_json(basic_json_t&, CompatibleType&&)` method - @liveexample{The following code shows the constructor with an @ref object_t - parameter.,basic_json__object_t} + @tparam U = `uncvref_t` - @sa @ref basic_json(const CompatibleObjectType&) -- create an object value - from a compatible STL container + @param[in] val the value to be forwarded - @since version 1.0.0 - */ - basic_json(const object_t& val) - : m_type(value_t::object), m_value(val) - {} + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. - /*! - @brief create an object (implicit) - - Create an object JSON value with a given content. This constructor allows - any type that can be used to construct values of type @ref object_t. - Examples include the types `std::map` and `std::unordered_map`. - - @tparam CompatibleObjectType an object type whose `key_type` and - `value_type` is compatible to @ref object_t - - @param[in] val a value for the object - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for object value fails + @throw what `json_serializer::to_json()` throws @liveexample{The following code shows the constructor with several - compatible object type parameters.,basic_json__CompatibleObjectType} + compatible types.,basic_json__CompatibleType} - @sa @ref basic_json(const object_t&) -- create an object value - - @since version 1.0.0 + @since version 2.1.0 */ - template ::value and - std::is_constructible::value, int>::type - = 0> - basic_json(const CompatibleObjectType& val) - : m_type(value_t::object) + template, + detail::enable_if_t::value and + not std::is_same::value and + not detail::is_basic_json_nested_type< + basic_json_t, U>::value and + detail::has_to_json::value, + int> = 0> + basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer::to_json( + std::declval(), std::forward(val)))) { - using std::begin; - using std::end; - m_value.object = create(begin(val), end(val)); + JSONSerializer::to_json(*this, std::forward(val)); + assert_invariant(); } - /*! - @brief create an array (explicit) - - Create an array JSON value with a given content. - - @param[in] val a value for the array - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for array value fails - - @liveexample{The following code shows the constructor with an @ref array_t - parameter.,basic_json__array_t} - - @sa @ref basic_json(const CompatibleArrayType&) -- create an array value - from a compatible STL containers - - @since version 1.0.0 - */ - basic_json(const array_t& val) - : m_type(value_t::array), m_value(val) - {} - - /*! - @brief create an array (implicit) - - Create an array JSON value with a given content. This constructor allows - any type that can be used to construct values of type @ref array_t. - Examples include the types `std::vector`, `std::list`, and `std::set`. - - @tparam CompatibleArrayType an object type whose `value_type` is compatible - to @ref array_t - - @param[in] val a value for the array - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for array value fails - - @liveexample{The following code shows the constructor with several - compatible array type parameters.,basic_json__CompatibleArrayType} - - @sa @ref basic_json(const array_t&) -- create an array value - - @since version 1.0.0 - */ - template ::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> - basic_json(const CompatibleArrayType& val) - : m_type(value_t::array) - { - using std::begin; - using std::end; - m_value.array = create(begin(val), end(val)); - } - - /*! - @brief create a string (explicit) - - Create an string JSON value with a given content. - - @param[in] val a value for the string - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for string value fails - - @liveexample{The following code shows the constructor with an @ref string_t - parameter.,basic_json__string_t} - - @sa @ref basic_json(const typename string_t::value_type*) -- create a - string value from a character pointer - @sa @ref basic_json(const CompatibleStringType&) -- create a string value - from a compatible string container - - @since version 1.0.0 - */ - basic_json(const string_t& val) - : m_type(value_t::string), m_value(val) - {} - - /*! - @brief create a string (explicit) - - Create a string JSON value with a given content. - - @param[in] val a literal value for the string - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for string value fails - - @liveexample{The following code shows the constructor with string literal - parameter.,basic_json__string_t_value_type} - - @sa @ref basic_json(const string_t&) -- create a string value - @sa @ref basic_json(const CompatibleStringType&) -- create a string value - from a compatible string container - - @since version 1.0.0 - */ - basic_json(const typename string_t::value_type* val) - : basic_json(string_t(val)) - {} - - /*! - @brief create a string (implicit) - - Create a string JSON value with a given content. - - @param[in] val a value for the string - - @tparam CompatibleStringType an string type which is compatible to @ref - string_t - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for string value fails - - @liveexample{The following code shows the construction of a string value - from a compatible type.,basic_json__CompatibleStringType} - - @sa @ref basic_json(const string_t&) -- create a string value - @sa @ref basic_json(const typename string_t::value_type*) -- create a - string value from a character pointer - - @since version 1.0.0 - */ - template ::value, int>::type - = 0> - basic_json(const CompatibleStringType& val) - : basic_json(string_t(val)) - {} - - /*! - @brief create a boolean (explicit) - - Creates a JSON boolean type from a given value. - - @param[in] val a boolean value to store - - @complexity Constant. - - @liveexample{The example below demonstrates boolean - values.,basic_json__boolean_t} - - @since version 1.0.0 - */ - basic_json(boolean_t val) - : m_type(value_t::boolean), m_value(val) - {} - - /*! - @brief create an integer number (explicit) - - Create an integer number JSON value with a given content. - - @tparam T helper type to compare number_integer_t and int (not visible in) - the interface. - - @param[in] val an integer to create a JSON number from - - @note This constructor would have the same signature as @ref - basic_json(const int value), so we need to switch this one off in case - number_integer_t is the same as int. This is done via the helper type @a T. - - @complexity Constant. - - @liveexample{The example below shows the construction of a JSON integer - number value.,basic_json__number_integer_t} - - @sa @ref basic_json(const int) -- create a number value (integer) - @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number - value (integer) from a compatible number type - - @since version 1.0.0 - */ - template::value) - and std::is_same::value - , int>::type = 0> - basic_json(const number_integer_t val) - : m_type(value_t::number_integer), m_value(val) - {} - - /*! - @brief create an integer number from an enum type (explicit) - - Create an integer number JSON value with a given content. - - @param[in] val an integer to create a JSON number from - - @note This constructor allows to pass enums directly to a constructor. As - C++ has no way of specifying the type of an anonymous enum explicitly, we - can only rely on the fact that such values implicitly convert to int. As - int may already be the same type of number_integer_t, we may need to switch - off the constructor @ref basic_json(const number_integer_t). - - @complexity Constant. - - @liveexample{The example below shows the construction of a JSON integer - number value from an anonymous enum.,basic_json__const_int} - - @sa @ref basic_json(const number_integer_t) -- create a number value - (integer) - @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number - value (integer) from a compatible number type - - @since version 1.0.0 - */ - basic_json(const int val) - : m_type(value_t::number_integer), - m_value(static_cast(val)) - {} - - /*! - @brief create an integer number (implicit) - - Create an integer number JSON value with a given content. This constructor - allows any type that can be used to construct values of type @ref - number_integer_t. Examples may include the types `int`, `int32_t`, or - `short`. - - @tparam CompatibleNumberIntegerType an integer type which is compatible to - @ref number_integer_t. - - @param[in] val an integer to create a JSON number from - - @complexity Constant. - - @liveexample{The example below shows the construction of several JSON - integer number values from compatible - types.,basic_json__CompatibleIntegerNumberType} - - @sa @ref basic_json(const number_integer_t) -- create a number value - (integer) - @sa @ref basic_json(const int) -- create a number value (integer) - - @since version 1.0.0 - */ - template::value and - std::numeric_limits::is_integer, CompatibleNumberIntegerType>::type - = 0> - basic_json(const CompatibleNumberIntegerType val) noexcept - : m_type(value_t::number_integer), - m_value(static_cast(val)) - {} - - /*! - @brief create a floating-point number (explicit) - - Create a floating-point number JSON value with a given content. - - @param[in] val a floating-point value to create a JSON number from - - @note RFC 7159 , section 6 - disallows NaN values: - > Numeric values that cannot be represented in the grammar below (such - > as Infinity and NaN) are not permitted. - In case the parameter @a val is not a number, a JSON null value is - created instead. - - @complexity Constant. - - @liveexample{The following example creates several floating-point - values.,basic_json__number_float_t} - - @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number - value (floating-point) from a compatible number type - - @since version 1.0.0 - */ - basic_json(const number_float_t val) - : m_type(value_t::number_float), m_value(val) - { - // replace infinity and NAN by null - if (not std::isfinite(val)) - { - m_type = value_t::null; - m_value = json_value(); - } - } - - /*! - @brief create an floating-point number (implicit) - - Create an floating-point number JSON value with a given content. This - constructor allows any type that can be used to construct values of type - @ref number_float_t. Examples may include the types `float`. - - @tparam CompatibleNumberFloatType a floating-point type which is compatible - to @ref number_float_t. - - @param[in] val a floating-point to create a JSON number from - - @note RFC 7159 , section 6 - disallows NaN values: - > Numeric values that cannot be represented in the grammar below (such - > as Infinity and NaN) are not permitted. - In case the parameter @a val is not a number, a JSON null value is - created instead. - - @complexity Constant. - - @liveexample{The example below shows the construction of several JSON - floating-point number values from compatible - types.,basic_json__CompatibleNumberFloatType} - - @sa @ref basic_json(const number_float_t) -- create a number value - (floating-point) - - @since version 1.0.0 - */ - template::value and - std::is_floating_point::value>::type - > - basic_json(const CompatibleNumberFloatType val) noexcept - : basic_json(number_float_t(val)) - {} - /*! @brief create a container (array or object) from an initializer list @@ -1325,21 +2020,21 @@ class basic_json 1. If the list is empty, an empty JSON object value `{}` is created. 2. If the list consists of pairs whose first element is a string, a JSON - object value is created where the first elements of the pairs are treated - as keys and the second elements are as values. + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. 3. In all other cases, an array is created. The rules aim to create the best fit between a C++ initializer list and JSON values. The rationale is as follows: 1. The empty initializer list is written as `{}` which is exactly an empty - JSON object. + JSON object. 2. C++ has now way of describing mapped types other than to list a list of - pairs. As JSON requires that keys must be of type string, rule 2 is the - weakest constraint one can pose on initializer lists to interpret them as - an object. + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. 3. In all other cases, the initializer list could not be interpreted as - JSON object type, so interpreting it as JSON array type is safe. + JSON object type, so interpreting it as JSON array type is safe. With the rules described above, the following JSON values cannot be expressed by an initializer list: @@ -1362,20 +2057,20 @@ class basic_json used by the functions @ref array(std::initializer_list) and @ref object(std::initializer_list). - @param[in] manual_type internal parameter; when @a type_deduction is set to - `false`, the created JSON value will use the provided type (only @ref + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref value_t::array and @ref value_t::object are valid); when @a type_deduction is set to `true`, this parameter has no effect - @throw std::domain_error if @a type_deduction is `false`, @a manual_type is - `value_t::object`, but @a init contains an element which is not a pair + @throw std::domain_error if @a type_deduction is `false`, @a manual_type + is `value_t::object`, but @a init contains an element which is not a pair whose first element is a string; example: `"cannot create object from initializer list"` @complexity Linear in the size of the initializer list @a init. @liveexample{The example below shows how JSON values are created from - initializer lists,basic_json__list_init_t} + initializer lists.,basic_json__list_init_t} @sa @ref array(std::initializer_list) -- create a JSON array value from an initializer list @@ -1388,22 +2083,13 @@ class basic_json bool type_deduction = true, value_t manual_type = value_t::array) { - // the initializer list could describe an object - bool is_an_object = true; - // check if each element is an array with two elements whose first // element is a string - for (const auto& element : init) + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const basic_json & element) { - if (not element.is_array() or element.size() != 2 - or not element[0].is_string()) - { - // we found an element that makes it impossible to use the - // initializer list as object - is_an_object = false; - break; - } - } + return element.is_array() and element.size() == 2 and element[0].is_string(); + }); // adjust type if type deduction is not wanted if (not type_deduction) @@ -1417,7 +2103,7 @@ class basic_json // if object is wanted but impossible, throw an exception if (manual_type == value_t::object and not is_an_object) { - throw std::domain_error("cannot create object from initializer list"); + JSON_THROW(std::domain_error("cannot create object from initializer list")); } } @@ -1427,19 +2113,19 @@ class basic_json m_type = value_t::object; m_value = value_t::object; - assert(m_value.object != nullptr); - - for (auto& element : init) + std::for_each(init.begin(), init.end(), [this](const basic_json & element) { - m_value.object->emplace(std::move(*(element[0].m_value.string)), std::move(element[1])); - } + m_value.object->emplace(*(element[0].m_value.string), element[1]); + }); } else { // the initializer list describes an array -> create array m_type = value_t::array; - m_value.array = create(std::move(init)); + m_value.array = create(init); } + + assert_invariant(); } /*! @@ -1449,8 +2135,8 @@ class basic_json list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the initializer list is empty, the empty array `[]` is created. - @note This function is only needed to express two edge cases that cannot be - realized with the initializer list constructor (@ref + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref basic_json(std::initializer_list, bool, value_t)). These cases are: 1. creating an array whose elements are all pairs whose first element is a @@ -1466,7 +2152,7 @@ class basic_json @complexity Linear in the size of @a init. - @liveexample{The following code shows an example for the @ref array + @liveexample{The following code shows an example for the `array` function.,array} @sa @ref basic_json(std::initializer_list, bool, value_t) -- @@ -1493,8 +2179,8 @@ class basic_json related function @ref array(std::initializer_list), there are no cases which can only be expressed by this function. That is, any initializer list @a init can also be passed to the initializer list - constructor - @ref basic_json(std::initializer_list, bool, value_t). + constructor @ref basic_json(std::initializer_list, bool, + value_t). @param[in] init initializer list to create an object from (optional) @@ -1506,7 +2192,7 @@ class basic_json @complexity Linear in the size of @a init. - @liveexample{The following code shows an example for the @ref object + @liveexample{The following code shows an example for the `object` function.,object} @sa @ref basic_json(std::initializer_list, bool, value_t) -- @@ -1525,8 +2211,8 @@ class basic_json /*! @brief construct an array with count copies of given value - Constructs a JSON array value by creating @a cnt copies of a passed - value. In case @a cnt is `0`, an empty array is created. As postcondition, + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. As postcondition, `std::distance(begin(),end()) == cnt` holds. @param[in] cnt the number of JSON copies of @a val to create @@ -1544,6 +2230,7 @@ class basic_json : m_type(value_t::array) { m_value.array = create(cnt, val); + assert_invariant(); } /*! @@ -1554,8 +2241,8 @@ class basic_json - In case of primitive types (number, boolean, or string), @a first must be `begin()` and @a last must be `end()`. In this case, the value is copied. Otherwise, std::out_of_range is thrown. - - In case of structured types (array, object), the constructor behaves - as similar versions for `std::vector`. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector`. - In case of a null type, std::domain_error is thrown. @tparam InputIT an input iterator type (@ref iterator or @ref @@ -1564,14 +2251,17 @@ class basic_json @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** + @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @throw std::out_of_range if iterators are for a primitive type (number, boolean, or string) where an out of range error can be detected easily; example: `"iterators out of range"` @throw std::bad_alloc if allocation for object, array, or string fails - @throw std::domain_error if called with a null value; example: `"cannot use - construct with iterators from null"` + @throw std::domain_error if called with a null value; example: `"cannot + use construct with iterators from null"` @complexity Linear in distance between @a first and @a last. @@ -1580,31 +2270,35 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> - basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type) + template::value or + std::is_same::value, int>::type = 0> + basic_json(InputIT first, InputIT last) { + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); + // make sure iterator fits the current value if (first.m_object != last.m_object) { - throw std::domain_error("iterators are not compatible"); + JSON_THROW(std::domain_error("iterators are not compatible")); } + // copy type from first iterator + m_type = first.m_object->m_type; + // check if iterator range is complete for primitive values switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - throw std::out_of_range("iterators out of range"); + JSON_THROW(std::out_of_range("iterators out of range")); } break; } @@ -1619,50 +2313,90 @@ class basic_json { case value_t::number_integer: { - assert(first.m_object != nullptr); m_value.number_integer = first.m_object->m_value.number_integer; break; } + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + case value_t::number_float: { - assert(first.m_object != nullptr); m_value.number_float = first.m_object->m_value.number_float; break; } case value_t::boolean: { - assert(first.m_object != nullptr); m_value.boolean = first.m_object->m_value.boolean; break; } case value_t::string: { - assert(first.m_object != nullptr); m_value = *first.m_object->m_value.string; break; } case value_t::object: { - m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); break; } case value_t::array: { - m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); break; } default: { - assert(first.m_object != nullptr); - throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name()); + JSON_THROW(std::domain_error("cannot use construct with iterators from " + first.m_object->type_name())); } } + + assert_invariant(); + } + + /*! + @brief construct a JSON value given an input stream + + @param[in,out] i stream to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @deprecated This constructor is deprecated and will be removed in version + 3.0.0 to unify the interface of the library. Deserialization will be + done by stream operators or by calling one of the `parse` functions, + e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls + like `json j(i);` for an input stream @a i need to be replaced by + `json j = json::parse(i);`. See the example below. + + @liveexample{The example below demonstrates constructing a JSON value from + a `std::stringstream` with and without callback + function.,basic_json__istream} + + @since version 2.0.0, deprecated in version 2.0.3, to be removed in + version 3.0.0 + */ + JSON_DEPRECATED + explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) + { + *this = parser(i, cb).parse(); + assert_invariant(); } /////////////////////////////////////// @@ -1678,7 +2412,9 @@ class basic_json @complexity Linear in the size of @a other. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is linear. - As postcondition, it holds: `other == basic_json(other)`. @@ -1692,25 +2428,25 @@ class basic_json basic_json(const basic_json& other) : m_type(other.m_type) { + // check of passed value is valid + other.assert_invariant(); + switch (m_type) { case value_t::object: { - assert(other.m_value.object != nullptr); m_value = *other.m_value.object; break; } case value_t::array: { - assert(other.m_value.array != nullptr); m_value = *other.m_value.array; break; } case value_t::string: { - assert(other.m_value.string != nullptr); m_value = *other.m_value.string; break; } @@ -1727,6 +2463,12 @@ class basic_json break; } + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + case value_t::number_float: { m_value = other.m_value.number_float; @@ -1738,6 +2480,8 @@ class basic_json break; } } + + assert_invariant(); } /*! @@ -1762,23 +2506,30 @@ class basic_json : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) { + // check that passed value is valid + other.assert_invariant(); + // invalidate payload other.m_type = value_t::null; other.m_value = {}; + + assert_invariant(); } /*! @brief copy assignment Copy assignment operator. Copies a JSON value via the "copy and swap" - strategy: It is expressed in terms of the copy constructor, destructor, and - the swap() member function. + strategy: It is expressed in terms of the copy constructor, destructor, + and the swap() member function. @param[in] other value to copy from @complexity Linear. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is linear. @liveexample{The code below shows and example for the copy assignment. It @@ -1795,9 +2546,14 @@ class basic_json std::is_nothrow_move_assignable::value ) { + // check that passed value is valid + other.assert_invariant(); + using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); + + assert_invariant(); return *this; } @@ -1808,7 +2564,9 @@ class basic_json @complexity Linear. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is linear. - All stored elements are destroyed and all memory is freed. @@ -1816,6 +2574,8 @@ class basic_json */ ~basic_json() { + assert_invariant(); + switch (m_type) { case value_t::object: @@ -1858,19 +2618,20 @@ class basic_json /////////////////////// /// @name object inspection + /// Functions to inspect the type of a JSON value. /// @{ /*! @brief serialization Serialization function for JSON values. The function tries to mimic - Python's @p json.dumps() function, and currently supports its @p indent + Python's `json.dumps()` function, and currently supports its @a indent parameter. - @param[in] indent if indent is nonnegative, then array elements and object - members will be pretty-printed with that indent level. An indent level of 0 - will only insert newlines. -1 (the default) selects the most compact - representation + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. @return string containing the serialization of the JSON value @@ -1909,12 +2670,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref type() for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON types.,type} @since version 1.0.0 */ - value_t type() const noexcept + constexpr value_t type() const noexcept { return m_type; } @@ -1930,12 +2694,21 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_primitive for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON types.,is_primitive} + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number + @since version 1.0.0 */ - bool is_primitive() const noexcept + constexpr bool is_primitive() const noexcept { return is_null() or is_string() or is_boolean() or is_number(); } @@ -1950,12 +2723,19 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_structured for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON types.,is_structured} + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object + @since version 1.0.0 */ - bool is_structured() const noexcept + constexpr bool is_structured() const noexcept { return is_array() or is_object(); } @@ -1969,12 +2749,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_null for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON types.,is_null} @since version 1.0.0 */ - bool is_null() const noexcept + constexpr bool is_null() const noexcept { return m_type == value_t::null; } @@ -1988,12 +2771,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_boolean for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON types.,is_boolean} @since version 1.0.0 */ - bool is_boolean() const noexcept + constexpr bool is_boolean() const noexcept { return m_type == value_t::boolean; } @@ -2004,20 +2790,26 @@ class basic_json This function returns true iff the JSON value is a number. This includes both integer and floating-point values. - @return `true` if type is number (regardless whether integer or - floating-type), `false` otherwise. + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. @complexity Constant. - @liveexample{The following code exemplifies @ref is_number for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON types.,is_number} - @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ - bool is_number() const noexcept + constexpr bool is_number() const noexcept { return is_number_integer() or is_number_float(); } @@ -2025,45 +2817,84 @@ class basic_json /*! @brief return whether value is an integer number - This function returns true iff the JSON value is an integer number. This - excludes floating-point values. + This function returns true iff the JSON value is an integer or unsigned + integer number. This excludes floating-point values. - @return `true` if type is an integer number, `false` otherwise. + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. @complexity Constant. - @liveexample{The following code exemplifies @ref is_number_integer for all + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all JSON types.,is_number_integer} @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 */ - bool is_number_integer() const noexcept + constexpr bool is_number_integer() const noexcept { - return m_type == value_t::number_integer; + return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true iff the JSON value is an unsigned integer + number. This excludes floating-point and (signed) integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; } /*! @brief return whether value is a floating-point number This function returns true iff the JSON value is a floating-point number. - This excludes integer values. + This excludes integer and unsigned integer values. @return `true` if type is a floating-point number, `false` otherwise. @complexity Constant. - @liveexample{The following code exemplifies @ref is_number_float for all + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all JSON types.,is_number_float} @sa @ref is_number() -- check if value is number @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @since version 1.0.0 */ - bool is_number_float() const noexcept + constexpr bool is_number_float() const noexcept { return m_type == value_t::number_float; } @@ -2077,12 +2908,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_object for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON types.,is_object} @since version 1.0.0 */ - bool is_object() const noexcept + constexpr bool is_object() const noexcept { return m_type == value_t::object; } @@ -2096,12 +2930,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_array for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON types.,is_array} @since version 1.0.0 */ - bool is_array() const noexcept + constexpr bool is_array() const noexcept { return m_type == value_t::array; } @@ -2115,12 +2952,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_string for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON types.,is_string} @since version 1.0.0 */ - bool is_string() const noexcept + constexpr bool is_string() const noexcept { return m_type == value_t::string; } @@ -2139,12 +2979,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies @ref is_discarded for all JSON + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON types.,is_discarded} @since version 1.0.0 */ - bool is_discarded() const noexcept + constexpr bool is_discarded() const noexcept { return m_type == value_t::discarded; } @@ -2159,12 +3002,15 @@ class basic_json @complexity Constant. - @liveexample{The following code exemplifies the value_t operator for all - JSON types.,operator__value_t} + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} @since version 1.0.0 */ - operator value_t() const noexcept + constexpr operator value_t() const noexcept { return m_type; } @@ -2176,251 +3022,97 @@ class basic_json // value access // ////////////////// - /// get an object (explicit) - template ::value and - std::is_convertible::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_object()) - { - assert(m_value.object != nullptr); - return T(m_value.object->begin(), m_value.object->end()); - } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } - } - - /// get an object (explicit) - object_t get_impl(object_t*) const - { - if (is_object()) - { - assert(m_value.object != nullptr); - return *(m_value.object); - } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } - } - - /// get an array (explicit) - template ::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_array()) - { - T to_vector; - assert(m_value.array != nullptr); - std::transform(m_value.array->begin(), m_value.array->end(), - std::inserter(to_vector, to_vector.end()), [](basic_json i) - { - return i.get(); - }); - return to_vector; - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get an array (explicit) - template ::value and - not std::is_same::value - , int>::type = 0> - std::vector get_impl(std::vector*) const - { - if (is_array()) - { - std::vector to_vector; - assert(m_value.array != nullptr); - to_vector.reserve(m_value.array->size()); - std::transform(m_value.array->begin(), m_value.array->end(), - std::inserter(to_vector, to_vector.end()), [](basic_json i) - { - return i.get(); - }); - return to_vector; - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get an array (explicit) - template ::value and - not has_mapped_type::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_array()) - { - assert(m_value.array != nullptr); - return T(m_value.array->begin(), m_value.array->end()); - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get an array (explicit) - array_t get_impl(array_t*) const - { - if (is_array()) - { - assert(m_value.array != nullptr); - return *(m_value.array); - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get a string (explicit) - template ::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_string()) - { - assert(m_value.string != nullptr); - return *m_value.string; - } - else - { - throw std::domain_error("type must be string, but is " + type_name()); - } - } - - /// get a number (explicit) - template::value - , int>::type = 0> - T get_impl(T*) const - { - switch (m_type) - { - case value_t::number_integer: - { - return static_cast(m_value.number_integer); - } - - case value_t::number_float: - { - return static_cast(m_value.number_float); - } - - default: - { - throw std::domain_error("type must be number, but is " + type_name()); - } - } - } - /// get a boolean (explicit) - boolean_t get_impl(boolean_t*) const + boolean_t get_impl(boolean_t* /*unused*/) const { if (is_boolean()) { return m_value.boolean; } - else - { - throw std::domain_error("type must be boolean, but is " + type_name()); - } + + JSON_THROW(std::domain_error("type must be boolean, but is " + type_name())); } /// get a pointer to the value (object) - object_t* get_impl_ptr(object_t*) noexcept + object_t* get_impl_ptr(object_t* /*unused*/) noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (object) - const object_t* get_impl_ptr(const object_t*) const noexcept + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept { return is_object() ? m_value.object : nullptr; } /// get a pointer to the value (array) - array_t* get_impl_ptr(array_t*) noexcept + array_t* get_impl_ptr(array_t* /*unused*/) noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (array) - const array_t* get_impl_ptr(const array_t*) const noexcept + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept { return is_array() ? m_value.array : nullptr; } /// get a pointer to the value (string) - string_t* get_impl_ptr(string_t*) noexcept + string_t* get_impl_ptr(string_t* /*unused*/) noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (string) - const string_t* get_impl_ptr(const string_t*) const noexcept + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept { return is_string() ? m_value.string : nullptr; } /// get a pointer to the value (boolean) - boolean_t* get_impl_ptr(boolean_t*) noexcept + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (boolean) - const boolean_t* get_impl_ptr(const boolean_t*) const noexcept + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept { return is_boolean() ? &m_value.boolean : nullptr; } /// get a pointer to the value (integer number) - number_integer_t* get_impl_ptr(number_integer_t*) noexcept + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } /// get a pointer to the value (integer number) - const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept { return is_number_integer() ? &m_value.number_integer : nullptr; } + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + /// get a pointer to the value (floating-point number) - number_float_t* get_impl_ptr(number_float_t*) noexcept + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept { return is_number_float() ? &m_value.number_float : nullptr; } /// get a pointer to the value (floating-point number) - const number_float_t* get_impl_ptr(const number_float_t*) const noexcept + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept { return is_number_float() ? &m_value.number_float : nullptr; } @@ -2439,41 +3131,79 @@ class basic_json template static ReferenceType get_ref_impl(ThisType& obj) { - // delegate the call to get_ptr<>() + // helper type using PointerType = typename std::add_pointer::type; + + // delegate the call to get_ptr<>() auto ptr = obj.template get_ptr(); if (ptr != nullptr) { return *ptr; } - else - { - throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + - obj.type_name()); - } + + JSON_THROW(std::domain_error("incompatible ReferenceType for get_ref, actual type is " + + obj.type_name())); } public: - /// @name value access + /// Direct access to the stored value of a JSON value. /// @{ + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template < + typename BasicJsonType, + detail::enable_if_t::type, + basic_json_t>::value, + int> = 0 > + basic_json get() const + { + return *this; + } + /*! @brief get a value (explicit) - Explicit type conversion between the JSON value and a compatible value. + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. - @tparam ValueType non-pointer type compatible to the JSON value, for - instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode - @return copy of the JSON value, converted to type @a ValueType + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const @ref basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const @ref basic_json&)` - @throw std::domain_error in case passed type @a ValueType is incompatible - to JSON; example: `"type must be object, but is null"` + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type - @complexity Linear in the size of the JSON value. + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws @liveexample{The example below shows several conversions from JSON values to other types. There a few things to note: (1) Floating-point numbers can @@ -2482,23 +3212,75 @@ class basic_json associative containers such as `std::unordered_map`.,get__ValueType_const} - @internal - The idea of using a casted null pointer to choose the correct - implementation is from . - @endinternal - - @sa @ref operator ValueType() const for implicit conversion - @sa @ref get() for pointer-member access - - @since version 1.0.0 + @since version 2.1.0 */ - template::value - , int>::type = 0> - ValueType get() const + template < + typename ValueTypeCV, + typename ValueType = detail::uncvref_t, + detail::enable_if_t < + not std::is_same::value and + detail::has_from_json::value and + not detail::has_non_default_from_json::value, + int > = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) { - return get_impl(static_cast(nullptr)); + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + static_assert(std::is_default_constructible::value, + "types must be DefaultConstructible when used with get()"); + + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const @ref basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < + typename ValueTypeCV, + typename ValueType = detail::uncvref_t, + detail::enable_if_t::value and + detail::has_non_default_from_json::value, int> = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return JSONSerializer::from_json(*this); } /*! @@ -2507,11 +3289,12 @@ class basic_json Explicit pointer access to the internally stored JSON value. No copies are made. - @warning The pointer becomes invalid if the underlying JSON object changes. + @warning The pointer becomes invalid if the underlying JSON object + changes. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref - number_float_t. + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @@ -2527,10 +3310,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr @@ -2541,11 +3322,9 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value - , int>::type = 0> - const PointerType get() const noexcept + template::value, int>::type = 0> + constexpr const PointerType get() const noexcept { // delegate the call to get_ptr return get_ptr(); @@ -2561,8 +3340,9 @@ class basic_json state. @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref - number_float_t. + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. @return pointer to the internally stored JSON value if the requested pointer type @a PointerType fits to the JSON value; `nullptr` otherwise @@ -2576,12 +3356,25 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> PointerType get_ptr() noexcept { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); + // delegate the call to get_impl_ptr<>() return get_impl_ptr(static_cast(nullptr)); } @@ -2590,13 +3383,26 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template::value - and std::is_const::type>::value - , int>::type = 0> - const PointerType get_ptr() const noexcept + template::value and + std::is_const::type>::value, int>::type = 0> + constexpr const PointerType get_ptr() const noexcept { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); + // delegate the call to get_impl_ptr<>() const return get_impl_ptr(static_cast(nullptr)); } @@ -2604,15 +3410,15 @@ class basic_json /*! @brief get a reference value (implicit) - Implict reference access to the internally stored JSON value. No copies are - made. + Implicit reference access to the internally stored JSON value. No copies + are made. @warning Writing data to the referee of the result yields an undefined state. @tparam ReferenceType reference type; must be a reference to @ref array_t, @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or - @ref number_float_t. + @ref number_float_t. Enforced by static assertion. @return reference to the internally stored JSON value if the requested reference type @a ReferenceType fits to the JSON value; throws @@ -2627,10 +3433,8 @@ class basic_json @since version 1.1.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -2641,11 +3445,9 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + template::value and + std::is_const::type>::value, int>::type = 0> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -2655,8 +3457,8 @@ class basic_json /*! @brief get a value (implicit) - Implicit type conversion between the JSON value and a compatible value. The - call is realized by calling @ref get() const. + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. @tparam ValueType non-pointer type compatible to the JSON value, for instance `int` for JSON integer numbers, `bool` for JSON booleans, or @@ -2680,11 +3482,10 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename - std::enable_if < - not std::is_pointer::value - and not std::is_same::value -#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 + template < typename ValueType, typename std::enable_if < + not std::is_pointer::value and + not std::is_same::value +#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 and not std::is_same>::value #endif , int >::type = 0 > @@ -2702,6 +3503,7 @@ class basic_json //////////////////// /// @name element access + /// Access to the JSON value. /// @{ /*! @@ -2722,7 +3524,7 @@ class basic_json @complexity Constant. @liveexample{The example below shows how array elements can be read and - written using at.,at__size_type} + written using `at()`.,at__size_type} @since version 1.0.0 */ @@ -2731,28 +3533,27 @@ class basic_json // at only works for arrays if (is_array()) { - try + JSON_TRY { - assert(m_value.array != nullptr); return m_value.array->at(idx); } - catch (std::out_of_range&) + JSON_CATCH (std::out_of_range&) { // create better exception explanation - throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); } } else { - throw std::domain_error("cannot use at() with " + type_name()); + JSON_THROW(std::domain_error("cannot use at() with " + type_name())); } } /*! @brief access specified array element with bounds checking - Returns a const reference to the element at specified location @a idx, with - bounds checking. + Returns a const reference to the element at specified location @a idx, + with bounds checking. @param[in] idx index of the element to access @@ -2766,7 +3567,7 @@ class basic_json @complexity Constant. @liveexample{The example below shows how array elements can be read using - at.,at__size_type_const} + `at()`.,at__size_type_const} @since version 1.0.0 */ @@ -2775,20 +3576,19 @@ class basic_json // at only works for arrays if (is_array()) { - try + JSON_TRY { - assert(m_value.array != nullptr); return m_value.array->at(idx); } - catch (std::out_of_range&) + JSON_CATCH (std::out_of_range&) { // create better exception explanation - throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); + JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); } } else { - throw std::domain_error("cannot use at() with " + type_name()); + JSON_THROW(std::domain_error("cannot use at() with " + type_name())); } } @@ -2810,7 +3610,7 @@ class basic_json @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and - written using at.,at__object_t_key_type} + written using `at()`.,at__object_t_key_type} @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @@ -2823,28 +3623,27 @@ class basic_json // at only works for objects if (is_object()) { - try + JSON_TRY { - assert(m_value.object != nullptr); return m_value.object->at(key); } - catch (std::out_of_range&) + JSON_CATCH (std::out_of_range&) { // create better exception explanation - throw std::out_of_range("key '" + key + "' not found"); + JSON_THROW(std::out_of_range("key '" + key + "' not found")); } } else { - throw std::domain_error("cannot use at() with " + type_name()); + JSON_THROW(std::domain_error("cannot use at() with " + type_name())); } } /*! @brief access specified object element with bounds checking - Returns a const reference to the element at with specified key @a key, with - bounds checking. + Returns a const reference to the element at with specified key @a key, + with bounds checking. @param[in] key key of the element to access @@ -2858,7 +3657,7 @@ class basic_json @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using - at.,at__object_t_key_type_const} + `at()`.,at__object_t_key_type_const} @sa @ref operator[](const typename object_t::key_type&) for unchecked access by reference @@ -2871,20 +3670,19 @@ class basic_json // at only works for objects if (is_object()) { - try + JSON_TRY { - assert(m_value.object != nullptr); return m_value.object->at(key); } - catch (std::out_of_range&) + JSON_CATCH (std::out_of_range&) { // create better exception explanation - throw std::out_of_range("key '" + key + "' not found"); + JSON_THROW(std::out_of_range("key '" + key + "' not found")); } } else { - throw std::domain_error("cannot use at() with " + type_name()); + JSON_THROW(std::domain_error("cannot use at() with " + type_name())); } } @@ -2901,42 +3699,43 @@ class basic_json @return reference to the element at index @a idx - @throw std::domain_error if JSON is not an array or null; example: `"cannot - use operator[] with null"` + @throw std::domain_error if JSON is not an array or null; example: + `"cannot use operator[] with string"` @complexity Constant if @a idx is in the range of the array. Otherwise linear in `idx - size()`. @liveexample{The example below shows how array elements can be read and - written using [] operator. Note the addition of `null` + written using `[]` operator. Note the addition of `null` values.,operatorarray__size_type} @since version 1.0.0 */ reference operator[](size_type idx) { - // implicitly convert null to object + // implicitly convert null value to an empty array if (is_null()) { m_type = value_t::array; m_value.array = create(); + assert_invariant(); } - // [] only works for arrays + // operator[] only works for arrays if (is_array()) { - assert(m_value.array != nullptr); - for (size_t i = m_value.array->size(); i <= idx; ++i) + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) { - m_value.array->push_back(basic_json()); + m_value.array->insert(m_value.array->end(), + idx - m_value.array->size() + 1, + basic_json()); } return m_value.array->operator[](idx); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -2954,22 +3753,19 @@ class basic_json @complexity Constant. @liveexample{The example below shows how array elements can be read using - the [] operator.,operatorarray__size_type_const} + the `[]` operator.,operatorarray__size_type_const} @since version 1.0.0 */ const_reference operator[](size_type idx) const { - // at only works for arrays + // const operator[] only works for arrays if (is_array()) { - assert(m_value.array != nullptr); return m_value.array->operator[](idx); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -2986,12 +3782,12 @@ class basic_json @return reference to the element at key @a key @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with null"` + `"cannot use operator[] with string"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and - written using the [] operator.,operatorarray__key_type} + written using the `[]` operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3001,23 +3797,21 @@ class basic_json */ reference operator[](const typename object_t::key_type& key) { - // implicitly convert null to object + // implicitly convert null value to an empty object if (is_null()) { m_type = value_t::object; m_value.object = create(); + assert_invariant(); } - // [] only works for objects + // operator[] only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->operator[](key); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -3033,13 +3827,16 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using - the [] operator.,operatorarray__key_type_const} + the `[]` operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3049,17 +3846,14 @@ class basic_json */ const_reference operator[](const typename object_t::key_type& key) const { - // [] only works for objects + // const operator[] only works for objects if (is_object()) { - assert(m_value.object != nullptr); assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -3076,12 +3870,12 @@ class basic_json @return reference to the element at key @a key @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with null"` + `"cannot use operator[] with string"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and - written using the [] operator.,operatorarray__key_type} + written using the `[]` operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3116,7 +3910,7 @@ class basic_json @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using - the [] operator.,operatorarray__key_type_const} + the `[]` operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3144,12 +3938,12 @@ class basic_json @return reference to the element at key @a key @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with null"` + `"cannot use operator[] with string"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read and - written using the [] operator.,operatorarray__key_type} + written using the `[]` operator.,operatorarray__key_type} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3165,18 +3959,16 @@ class basic_json { m_type = value_t::object; m_value = value_t::object; + assert_invariant(); } // at only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->operator[](key); } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @@ -3192,13 +3984,16 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @complexity Logarithmic in the size of the container. @liveexample{The example below shows how object elements can be read using - the [] operator.,operatorarray__key_type_const} + the `[]` operator.,operatorarray__key_type_const} @sa @ref at(const typename object_t::key_type&) for access by reference with range checking @@ -3212,21 +4007,18 @@ class basic_json // at only works for objects if (is_object()) { - assert(m_value.object != nullptr); assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } + + JSON_THROW(std::domain_error("cannot use operator[] with " + type_name())); } /*! @brief access specified object element with default value - Returns either a copy of an object's element at the specified key @a key or - a given default value if no element with key @a key exists. + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. The function is basically equivalent to executing @code {.cpp} @@ -3270,10 +4062,8 @@ class basic_json @since version 1.0.0 */ - template ::value - , int>::type = 0> + template::value, int>::type = 0> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3285,26 +4075,95 @@ class basic_json { return *it; } - else - { - return default_value; - } + + return default_value; } else { - throw std::domain_error("cannot use value() with " + type_name()); + JSON_THROW(std::domain_error("cannot use value() with " + type_name())); } } /*! @brief overload for a default value of type const char* - @copydoc basic_json::value() + @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const */ string_t value(const typename object_t::key_type& key, const char* default_value) const { return value(key, string_t(default_value)); } + /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(std::out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw std::domain_error if JSON is not an object; example: `"cannot use + value() with null"` + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, ValueType default_value) const + { + // at only works for objects + if (is_object()) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this); + } + JSON_CATCH (std::out_of_range&) + { + return default_value; + } + } + + JSON_THROW(std::domain_error("cannot use value() with " + type_name())); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + /*! @brief access the first element @@ -3312,16 +4171,21 @@ class basic_json container `c`, the expression `c.front()` is equivalent to `*c.begin()`. @return In case of a structured type (array or object), a reference to the - first element is returned. In cast of number, string, or boolean values, a + first element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. - @note Calling `front` on an empty container is undefined. + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. - @throw std::out_of_range when called on null value + @throw std::out_of_range when called on `null` value - @liveexample{The following code shows an example for @ref front.,front} + @liveexample{The following code shows an example for `front()`.,front} + + @sa @ref back() -- access the last element @since version 1.0.0 */ @@ -3342,20 +4206,29 @@ class basic_json @brief access the last element Returns a reference to the last element in the container. For a JSON - container `c`, the expression `c.back()` is equivalent to `{ auto tmp = - c.end(); --tmp; return *tmp; }`. + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode @return In case of a structured type (array or object), a reference to the - last element is returned. In cast of number, string, or boolean values, a + last element is returned. In case of number, string, or boolean values, a reference to the value is returned. @complexity Constant. - @note Calling `back` on an empty container is undefined. + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. - @throw std::out_of_range when called on null value. + @throw std::out_of_range when called on `null` value. - @liveexample{The following code shows an example for @ref back.,back} + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element @since version 1.0.0 */ @@ -3379,102 +4252,103 @@ class basic_json /*! @brief remove element given an iterator - Removes the element specified by iterator @a pos. Invalidates iterators and - references at or after the point of the erase, including the end() - iterator. The iterator @a pos must be valid and dereferenceable. Thus the - end() iterator (which is valid, but is not dereferenceable) cannot be used - as a value for @a pos. + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. - If called on a primitive type other than null, the resulting JSON value + If called on a primitive type other than `null`, the resulting JSON value will be `null`. @param[in] pos iterator to the element to remove - @return Iterator following the last removed element. If the iterator @a pos - refers to the last element, the end() iterator is returned. + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator - @throw std::domain_error if called on a `null` value; example: `"cannot use - erase() with null"` + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw std::domain_error if called on a `null` value; example: `"cannot + use erase() with null"` @throw std::domain_error if called on an iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"` @throw std::out_of_range if called on a primitive type with invalid - iterator (i.e., any iterator which is not end()); example: `"iterator out - of range"` + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` @complexity The complexity depends on the type: - objects: amortized constant - - arrays: linear in distance between pos and the end of the container + - arrays: linear in distance between @a pos and the end of the container - strings: linear in the length of the string - other types: constant - @liveexample{The example shows the result of erase for different JSON + @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType} - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the - given range + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at the - given index + @sa @ref erase(const size_type) -- removes the element from an array at + the given index @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> - InteratorType erase(InteratorType pos) + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType pos) { // make sure iterator fits the current value if (this != pos.m_object) { - throw std::domain_error("iterator does not fit current value"); + JSON_THROW(std::domain_error("iterator does not fit current value")); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { if (not pos.m_it.primitive_iterator.is_begin()) { - throw std::out_of_range("iterator out of range"); + JSON_THROW(std::out_of_range("iterator out of range")); } if (is_string()) { - delete m_value.string; + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; + assert_invariant(); break; } case value_t::object: { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); break; } case value_t::array: { - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); break; } default: { - throw std::domain_error("cannot use erase() with " + type_name()); + JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); } } @@ -3484,23 +4358,25 @@ class basic_json /*! @brief remove elements given an iterator range - Removes the element specified by the range `[first; last)`. Invalidates - iterators and references at or after the point of the erase, including the - end() iterator. The iterator @a first does not need to be dereferenceable - if `first == last`: erasing an empty range is a no-op. + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. - If called on a primitive type other than null, the resulting JSON value + If called on a primitive type other than `null`, the resulting JSON value will be `null`. @param[in] first iterator to the beginning of the range to remove @param[in] last iterator past the end of the range to remove @return Iterator following the last removed element. If the iterator @a - second refers to the last element, the end() iterator is returned. + second refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator - @throw std::domain_error if called on a `null` value; example: `"cannot use - erase() with null"` + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw std::domain_error if called on a `null` value; example: `"cannot + use erase() with null"` @throw std::domain_error if called on iterators which does not belong to the current JSON value; example: `"iterators do not fit current value"` @throw std::out_of_range if called on a primitive type with invalid @@ -3514,58 +4390,59 @@ class basic_json - strings: linear in the length of the string - other types: constant - @liveexample{The example shows the result of erase for different JSON + @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType_IteratorType} - @sa @ref erase(InteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType) -- removes the element at a given position @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at the - given index + @sa @ref erase(const size_type) -- removes the element from an array at + the given index @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> - InteratorType erase(InteratorType first, InteratorType last) + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) { - throw std::domain_error("iterators do not fit current value"); + JSON_THROW(std::domain_error("iterators do not fit current value")); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { case value_t::boolean: case value_t::number_float: case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) { - throw std::out_of_range("iterators out of range"); + JSON_THROW(std::out_of_range("iterators out of range")); } if (is_string()) { - delete m_value.string; + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; + assert_invariant(); break; } case value_t::object: { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); break; @@ -3573,7 +4450,6 @@ class basic_json case value_t::array: { - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); break; @@ -3581,7 +4457,7 @@ class basic_json default: { - throw std::domain_error("cannot use erase() with " + type_name()); + JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); } } @@ -3595,22 +4471,25 @@ class basic_json @param[in] key value of the elements to remove - @return Number of elements removed. If ObjectType is the default `std::map` - type, the return value will always be `0` (@a key was not found) or `1` (@a - key was found). + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. @throw std::domain_error when called on a type other than JSON object; example: `"cannot use erase() with null"` @complexity `log(size()) + count(key)` - @liveexample{The example shows the effect of erase.,erase__key_type} + @liveexample{The example shows the effect of `erase()`.,erase__key_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the - given range - @sa @ref erase(const size_type) -- removes the element from an array at the - given index + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const size_type) -- removes the element from an array at + the given index @since version 1.0.0 */ @@ -3619,13 +4498,10 @@ class basic_json // this erase only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->erase(key); } - else - { - throw std::domain_error("cannot use erase() with " + type_name()); - } + + JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); } /*! @@ -3637,16 +4513,16 @@ class basic_json @throw std::domain_error when called on a type other than JSON array; example: `"cannot use erase() with null"` - @throw std::out_of_range when `idx >= size()`; example: `"index out of - range"` + @throw std::out_of_range when `idx >= size()`; example: `"array index 17 + is out of range"` @complexity Linear in distance between @a idx and the end of the container. - @liveexample{The example shows the effect of erase.,erase__size_type} + @liveexample{The example shows the effect of `erase()`.,erase__size_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the - given range + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -3659,32 +4535,46 @@ class basic_json { if (idx >= size()) { - throw std::out_of_range("index out of range"); + JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); } - assert(m_value.array != nullptr); m_value.array->erase(m_value.array->begin() + static_cast(idx)); } else { - throw std::domain_error("cannot use erase() with " + type_name()); + JSON_THROW(std::domain_error("cannot use erase() with " + type_name())); } } + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + /*! @brief find an element in a JSON object Finds an element in a JSON object with key equivalent to @a key. If the - element is not found or the JSON value is not an object, end() is returned. + element is not found or the JSON value is not an object, end() is + returned. + + @note This method always returns @ref end() when executed on a JSON type + that is not an object. @param[in] key key value of the element to search for @return Iterator to an element with key equivalent to @a key. If no such - element is found, past-the-end (see end()) iterator is returned. + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. @complexity Logarithmic in the size of the JSON object. - @liveexample{The example shows how find is used.,find__key_type} + @liveexample{The example shows how `find()` is used.,find__key_type} @since version 1.0.0 */ @@ -3694,7 +4584,6 @@ class basic_json if (is_object()) { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->find(key); } @@ -3711,7 +4600,6 @@ class basic_json if (is_object()) { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->find(key); } @@ -3725,6 +4613,9 @@ class basic_json default `std::map` type, the return value will always be `0` (@a key was not found) or `1` (@a key was found). + @note This method always returns `0` when executed on a JSON type that is + not an object. + @param[in] key key value of the element to count @return Number of elements with key @a key. If the JSON value is not an @@ -3732,14 +4623,13 @@ class basic_json @complexity Logarithmic in the size of the JSON object. - @liveexample{The example shows how count is used.,count} + @liveexample{The example shows how `count()` is used.,count} @since version 1.0.0 */ size_type count(typename object_t::key_type key) const { // return 0 for all nonobject types - assert(not is_object() or m_value.object != nullptr); return is_object() ? m_value.object->count(key) : 0; } @@ -3764,14 +4654,20 @@ class basic_json @complexity Constant. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - @liveexample{The following code shows an example for @ref begin.,begin} + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ - iterator begin() + iterator begin() noexcept { iterator result(this); result.set_begin(); @@ -3781,7 +4677,7 @@ class basic_json /*! @copydoc basic_json::cbegin() */ - const_iterator begin() const + const_iterator begin() const noexcept { return cbegin(); } @@ -3797,15 +4693,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).begin()`. - @liveexample{The following code shows an example for @ref cbegin.,cbegin} + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ - const_iterator cbegin() const + const_iterator cbegin() const noexcept { const_iterator result(this); result.set_begin(); @@ -3823,14 +4725,20 @@ class basic_json @complexity Constant. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - @liveexample{The following code shows an example for @ref end.,end} + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ - iterator end() + iterator end() noexcept { iterator result(this); result.set_end(); @@ -3840,7 +4748,7 @@ class basic_json /*! @copydoc basic_json::cend() */ - const_iterator end() const + const_iterator end() const noexcept { return cend(); } @@ -3856,15 +4764,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).end()`. - @liveexample{The following code shows an example for @ref cend.,cend} + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ - const_iterator cend() const + const_iterator cend() const noexcept { const_iterator result(this); result.set_end(); @@ -3880,15 +4794,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the ReversibleContainer requirements: + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(end())`. - @liveexample{The following code shows an example for @ref rbegin.,rbegin} + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end @since version 1.0.0 */ - reverse_iterator rbegin() + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } @@ -3896,7 +4816,7 @@ class basic_json /*! @copydoc basic_json::crbegin() */ - const_reverse_iterator rbegin() const + const_reverse_iterator rbegin() const noexcept { return crbegin(); } @@ -3911,15 +4831,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the ReversibleContainer requirements: + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: - The complexity is constant. - Has the semantics of `reverse_iterator(begin())`. - @liveexample{The following code shows an example for @ref rend.,rend} + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ - reverse_iterator rend() + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } @@ -3927,7 +4853,7 @@ class basic_json /*! @copydoc basic_json::crend() */ - const_reverse_iterator rend() const + const_reverse_iterator rend() const noexcept { return crend(); } @@ -3942,15 +4868,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the ReversibleContainer requirements: + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).rbegin()`. - @liveexample{The following code shows an example for @ref crbegin.,crbegin} + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end @since version 1.0.0 */ - const_reverse_iterator crbegin() const + const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } @@ -3965,15 +4897,21 @@ class basic_json @complexity Constant. - @requirement This function satisfies the ReversibleContainer requirements: + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: - The complexity is constant. - Has the semantics of `const_cast(*this).rend()`. - @liveexample{The following code shows an example for @ref crend.,crend} + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ - const_reverse_iterator crend() const + const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } @@ -3987,9 +4925,9 @@ class basic_json @brief wrapper to access iterator member functions in range-based for This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a reference - to the JSON values is returned, so there is no access to the underlying - iterator. + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. @note The name of this function is not yet final and may change in the future. @@ -4026,24 +4964,32 @@ class basic_json defined as follows: Value type | return value ----------- | ------------- - null | @c true - boolean | @c false - string | @c false - number | @c false - object | result of function object_t::empty() - array | result of function array_t::empty() + null | `true` + boolean | `false` + string | `false` + number | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` - @complexity Constant, as long as @ref array_t and @ref object_t satisfy the - Container concept; that is, their empty() functions have constant + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant complexity. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of `begin() == end()`. - @liveexample{The following code uses @ref empty to check if a @ref json + @liveexample{The following code uses `empty()` to check if a JSON object contains any elements.,empty} + @sa @ref size() -- returns the number of elements + @since version 1.0.0 */ bool empty() const noexcept @@ -4058,13 +5004,13 @@ class basic_json case value_t::array: { - assert(m_value.array != nullptr); + // delegate call to array_t::empty() return m_value.array->empty(); } case value_t::object: { - assert(m_value.object != nullptr); + // delegate call to object_t::empty() return m_value.object->empty(); } @@ -4085,23 +5031,33 @@ class basic_json defined as follows: Value type | return value ----------- | ------------- - null | @c 0 - boolean | @c 1 - string | @c 1 - number | @c 1 + null | `0` + boolean | `1` + string | `1` + number | `1` object | result of function object_t::size() array | result of function array_t::size() - @complexity Constant, as long as @ref array_t and @ref object_t satisfy the - Container concept; that is, their size() functions have constant complexity. + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. - @requirement This function satisfies the Container requirements: + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of `std::distance(begin(), end())`. - @liveexample{The following code calls @ref size on the different value + @liveexample{The following code calls `size()` on the different value types.,size} + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements + @since version 1.0.0 */ size_type size() const noexcept @@ -4116,13 +5072,13 @@ class basic_json case value_t::array: { - assert(m_value.array != nullptr); + // delegate call to array_t::size() return m_value.array->size(); } case value_t::object: { - assert(m_value.object != nullptr); + // delegate call to object_t::size() return m_value.object->size(); } @@ -4145,25 +5101,29 @@ class basic_json defined as follows: Value type | return value ----------- | ------------- - null | @c 0 (same as size()) - boolean | @c 1 (same as size()) - string | @c 1 (same as size()) - number | @c 1 (same as size()) - object | result of function object_t::max_size() - array | result of function array_t::max_size() + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` - @complexity Constant, as long as @ref array_t and @ref object_t satisfy the - Container concept; that is, their max_size() functions have constant + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant complexity. - @requirement This function satisfies the Container requirements: + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: - The complexity is constant. - Has the semantics of returning `b.size()` where `b` is the largest possible JSON value. - @liveexample{The following code calls @ref max_size on the different value + @liveexample{The following code calls `max_size()` on the different value types. Note the output is implementation specific.,max_size} + @sa @ref size() -- returns the number of elements + @since version 1.0.0 */ size_type max_size() const noexcept @@ -4172,13 +5132,13 @@ class basic_json { case value_t::array: { - assert(m_value.array != nullptr); + // delegate call to array_t::max_size() return m_value.array->max_size(); } case value_t::object: { - assert(m_value.object != nullptr); + // delegate call to object_t::max_size() return m_value.object->max_size(); } @@ -4215,12 +5175,9 @@ class basic_json object | `{}` array | `[]` - @note Floating-point numbers are set to `0.0` which will be serialized to - `0`. The vale type remains @ref number_float_t. - @complexity Linear in the size of the JSON value. - @liveexample{The example below shows the effect of @ref clear to different + @liveexample{The example below shows the effect of `clear()` to different JSON types.,clear} @since version 1.0.0 @@ -4235,6 +5192,12 @@ class basic_json break; } + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + case value_t::number_float: { m_value.number_float = 0.0; @@ -4249,21 +5212,18 @@ class basic_json case value_t::string: { - assert(m_value.string != nullptr); m_value.string->clear(); break; } case value_t::array: { - assert(m_value.array != nullptr); m_value.array->clear(); break; } case value_t::object: { - assert(m_value.object != nullptr); m_value.object->clear(); break; } @@ -4282,16 +5242,16 @@ class basic_json function is called on a JSON null value, an empty array is created before appending @a val. - @param val the value to add to the JSON array + @param[in] val the value to add to the JSON array @throw std::domain_error when called on a type other than JSON array or null; example: `"cannot use push_back() with number"` @complexity Amortized constant. - @liveexample{The example shows how `push_back` and `+=` can be used to add - elements to a JSON array. Note how the `null` value was silently converted - to a JSON array.,push_back} + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} @since version 1.0.0 */ @@ -4300,7 +5260,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - throw std::domain_error("cannot use push_back() with " + type_name()); + JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); } // transform null object into an array @@ -4308,10 +5268,10 @@ class basic_json { m_type = value_t::array; m_value = value_t::array; + assert_invariant(); } // add element to array (move semantics) - assert(m_value.array != nullptr); m_value.array->push_back(std::move(val)); // invalidate object val.m_type = value_t::null; @@ -4336,7 +5296,7 @@ class basic_json // push_back only works for null objects or arrays if (not(is_null() or is_array())) { - throw std::domain_error("cannot use push_back() with " + type_name()); + JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); } // transform null object into an array @@ -4344,10 +5304,10 @@ class basic_json { m_type = value_t::array; m_value = value_t::array; + assert_invariant(); } // add element to array - assert(m_value.array != nullptr); m_value.array->push_back(val); } @@ -4365,8 +5325,8 @@ class basic_json @brief add an object to an object Inserts the given element @a val to the JSON object. If the function is - called on a JSON null value, an empty object is created before inserting @a - val. + called on a JSON null value, an empty object is created before inserting + @a val. @param[in] val the value to add to the JSON object @@ -4375,9 +5335,9 @@ class basic_json @complexity Logarithmic in the size of the container, O(log(`size()`)). - @liveexample{The example shows how `push_back` and `+=` can be used to add - elements to a JSON object. Note how the `null` value was silently converted - to a JSON object.,push_back__object_t__value} + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} @since version 1.0.0 */ @@ -4386,7 +5346,7 @@ class basic_json // push_back only works for null objects or objects if (not(is_null() or is_object())) { - throw std::domain_error("cannot use push_back() with " + type_name()); + JSON_THROW(std::domain_error("cannot use push_back() with " + type_name())); } // transform null object into an object @@ -4394,10 +5354,10 @@ class basic_json { m_type = value_t::object; m_value = value_t::object; + assert_invariant(); } // add element to array - assert(m_value.object != nullptr); m_value.object->insert(val); } @@ -4408,7 +5368,151 @@ class basic_json reference operator+=(const typename object_t::value_type& val) { push_back(val); - return operator[](val.first); + return *this; + } + + /*! + @brief add an object to an object + + This function allows to use `push_back` with an initializer list. In case + + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, + + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). + + @param init an initializer list + + @complexity Linear in the size of the initializer list @a init. + + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(std::initializer_list init) + { + if (is_object() and init.size() == 2 and init.begin()->is_string()) + { + const string_t key = *init.begin(); + push_back(typename object_t::value_type(key, *(init.begin() + 1))); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(std::initializer_list) + */ + reference operator+=(std::initializer_list init) + { + push_back(init); + return *this; + } + + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @throw std::domain_error when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8 + */ + template + void emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + JSON_THROW(std::domain_error("cannot use emplace_back() with " + type_name())); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + m_value.array->emplace_back(std::forward(args)...); + } + + /*! + @brief add an object to an object if key does not exist + + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. + + @throw std::domain_error when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} + + @since version 2.0.8 + */ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (not(is_null() or is_object())) + { + JSON_THROW(std::domain_error("cannot use emplace() with " + type_name())); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; } /*! @@ -4426,10 +5530,10 @@ class basic_json @throw std::domain_error if @a pos is not an iterator of *this; example: `"iterator does not fit current value"` - @complexity Constant plus linear in the distance between pos and end of the - container. + @complexity Constant plus linear in the distance between @a pos and end of + the container. - @liveexample{The example shows how insert is used.,insert} + @liveexample{The example shows how `insert()` is used.,insert} @since version 1.0.0 */ @@ -4441,19 +5545,16 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - throw std::domain_error("iterator does not fit current value"); + JSON_THROW(std::domain_error("iterator does not fit current value")); } // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); return result; } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } + + JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); } /*! @@ -4485,7 +5586,7 @@ class basic_json @complexity Linear in @a cnt plus linear in the distance between @a pos and end of the container. - @liveexample{The example shows how insert is used.,insert__count} + @liveexample{The example shows how `insert()` is used.,insert__count} @since version 1.0.0 */ @@ -4497,19 +5598,16 @@ class basic_json // check if iterator pos fits to this JSON value if (pos.m_object != this) { - throw std::domain_error("iterator does not fit current value"); + JSON_THROW(std::domain_error("iterator does not fit current value")); } // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); return result; } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } + + JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); } /*! @@ -4538,7 +5636,7 @@ class basic_json @complexity Linear in `std::distance(first, last)` plus linear in the distance between @a pos and end of the container. - @liveexample{The example shows how insert is used.,insert__range} + @liveexample{The example shows how `insert()` is used.,insert__range} @since version 1.0.0 */ @@ -4547,28 +5645,28 @@ class basic_json // insert only works for arrays if (not is_array()) { - throw std::domain_error("cannot use insert() with " + type_name()); + JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - throw std::domain_error("iterator does not fit current value"); + JSON_THROW(std::domain_error("iterator does not fit current value")); } + // check if range iterators belong to the same JSON object if (first.m_object != last.m_object) { - throw std::domain_error("iterators do not fit"); + JSON_THROW(std::domain_error("iterators do not fit")); } if (first.m_object == this or last.m_object == this) { - throw std::domain_error("passed iterators may not belong to container"); + JSON_THROW(std::domain_error("passed iterators may not belong to container")); } // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert( pos.m_it.array_iterator, first.m_it.array_iterator, @@ -4593,10 +5691,10 @@ class basic_json @return iterator pointing to the first element inserted, or @a pos if `ilist` is empty - @complexity Linear in `ilist.size()` plus linear in the distance between @a - pos and end of the container. + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. - @liveexample{The example shows how insert is used.,insert__ilist} + @liveexample{The example shows how `insert()` is used.,insert__ilist} @since version 1.0.0 */ @@ -4605,18 +5703,17 @@ class basic_json // insert only works for arrays if (not is_array()) { - throw std::domain_error("cannot use insert() with " + type_name()); + JSON_THROW(std::domain_error("cannot use insert() with " + type_name())); } // check if iterator pos fits to this JSON value if (pos.m_object != this) { - throw std::domain_error("iterator does not fit current value"); + JSON_THROW(std::domain_error("iterator does not fit current value")); } // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); return result; } @@ -4633,8 +5730,8 @@ class basic_json @complexity Constant. - @liveexample{The example below shows how JSON arrays can be - swapped.,swap__reference} + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} @since version 1.0.0 */ @@ -4647,6 +5744,7 @@ class basic_json { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); + assert_invariant(); } /*! @@ -4659,13 +5757,13 @@ class basic_json @param[in,out] other array to exchange the contents with - @throw std::domain_error when JSON value is not an array; example: `"cannot - use swap() with string"` + @throw std::domain_error when JSON value is not an array; example: + `"cannot use swap() with string"` @complexity Constant. - @liveexample{The example below shows how JSON values can be - swapped.,swap__array_t} + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} @since version 1.0.0 */ @@ -4674,12 +5772,11 @@ class basic_json // swap only works for arrays if (is_array()) { - assert(m_value.array != nullptr); std::swap(*(m_value.array), other); } else { - throw std::domain_error("cannot use swap() with " + type_name()); + JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); } } @@ -4698,8 +5795,8 @@ class basic_json @complexity Constant. - @liveexample{The example below shows how JSON values can be - swapped.,swap__object_t} + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} @since version 1.0.0 */ @@ -4708,12 +5805,11 @@ class basic_json // swap only works for objects if (is_object()) { - assert(m_value.object != nullptr); std::swap(*(m_value.object), other); } else { - throw std::domain_error("cannot use swap() with " + type_name()); + JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); } } @@ -4732,8 +5828,8 @@ class basic_json @complexity Constant. - @liveexample{The example below shows how JSON values can be - swapped.,swap__string_t} + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} @since version 1.0.0 */ @@ -4742,18 +5838,17 @@ class basic_json // swap only works for strings if (is_string()) { - assert(m_value.string != nullptr); std::swap(*(m_value.string), other); } else { - throw std::domain_error("cannot use swap() with " + type_name()); + JSON_THROW(std::domain_error("cannot use swap() with " + type_name())); } } /// @} - + public: ////////////////////////////////////////// // lexicographical comparison operators // ////////////////////////////////////////// @@ -4761,39 +5856,6 @@ class basic_json /// @name lexicographical comparison operators /// @{ - private: - /*! - @brief comparison operator for JSON types - - Returns an ordering that is similar to Python: - - order: null < boolean < number < object < array < string - - furthermore, each type is not smaller than itself - - @since version 1.0.0 - */ - friend bool operator<(const value_t lhs, const value_t rhs) - { - static constexpr std::array order = {{ - 0, // null - 3, // object - 4, // array - 5, // string - 1, // boolean - 2, // integer - 2 // float - } - }; - - // discarded values are not comparable - if (lhs == value_t::discarded or rhs == value_t::discarded) - { - return false; - } - - return order[static_cast(lhs)] < order[static_cast(rhs)]; - } - - public: /*! @brief comparison: equal @@ -4828,14 +5890,10 @@ class basic_json { case value_t::array: { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); return *lhs.m_value.array == *rhs.m_value.array; } case value_t::object: { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); return *lhs.m_value.object == *rhs.m_value.object; } case value_t::null: @@ -4844,8 +5902,6 @@ class basic_json } case value_t::string: { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); return *lhs.m_value.string == *rhs.m_value.string; } case value_t::boolean: @@ -4856,6 +5912,10 @@ class basic_json { return lhs.m_value.number_integer == rhs.m_value.number_integer; } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + } case value_t::number_float: { return lhs.m_value.number_float == rhs.m_value.number_float; @@ -4874,39 +5934,46 @@ class basic_json { return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + return false; } /*! @brief comparison: equal - - The functions compares the given JSON value against a null pointer. As the - null pointer can be used to initialize a JSON value to null, a comparison - of JSON value @a v with a null pointer should be equivalent to call - `v.is_null()`. - - @param[in] v JSON value to consider - @return whether @a v is null - - @complexity Constant. - - @liveexample{The example compares several JSON types to the null pointer. - ,operator__equal__nullptr_t} - - @since version 1.0.0 + @copydoc operator==(const_reference, const_reference) */ - friend bool operator==(const_reference v, std::nullptr_t) noexcept + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept { - return v.is_null(); + return (lhs == basic_json(rhs)); } /*! @brief comparison: equal - @copydoc operator==(const_reference, std::nullptr_t) + @copydoc operator==(const_reference, const_reference) */ - friend bool operator==(std::nullptr_t, const_reference v) noexcept + template::value, int>::type = 0> + friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept { - return v.is_null(); + return (basic_json(lhs) == rhs); } /*! @@ -4932,34 +5999,24 @@ class basic_json /*! @brief comparison: not equal - - The functions compares the given JSON value against a null pointer. As the - null pointer can be used to initialize a JSON value to null, a comparison - of JSON value @a v with a null pointer should be equivalent to call - `not v.is_null()`. - - @param[in] v JSON value to consider - @return whether @a v is not null - - @complexity Constant. - - @liveexample{The example compares several JSON types to the null pointer. - ,operator__notequal__nullptr_t} - - @since version 1.0.0 + @copydoc operator!=(const_reference, const_reference) */ - friend bool operator!=(const_reference v, std::nullptr_t) noexcept + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept { - return not v.is_null(); + return (lhs != basic_json(rhs)); } /*! @brief comparison: not equal - @copydoc operator!=(const_reference, std::nullptr_t) + @copydoc operator!=(const_reference, const_reference) */ - friend bool operator!=(std::nullptr_t, const_reference v) noexcept + template::value, int>::type = 0> + friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept { - return not v.is_null(); + return (basic_json(lhs) != rhs); } /*! @@ -4997,14 +6054,10 @@ class basic_json { case value_t::array: { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); return *lhs.m_value.array < *rhs.m_value.array; } case value_t::object: { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); return *lhs.m_value.object < *rhs.m_value.object; } case value_t::null: @@ -5013,8 +6066,6 @@ class basic_json } case value_t::string: { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); return *lhs.m_value.string < *rhs.m_value.string; } case value_t::boolean: @@ -5025,6 +6076,10 @@ class basic_json { return lhs.m_value.number_integer < rhs.m_value.number_integer; } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + } case value_t::number_float: { return lhs.m_value.number_float < rhs.m_value.number_float; @@ -5037,13 +6092,27 @@ class basic_json } else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) { - return static_cast(lhs.m_value.number_integer) < - rhs.m_value.number_float; + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; } else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) { - return lhs.m_value.number_float < - static_cast(rhs.m_value.number_integer); + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; } // We only reach this line if we cannot compare values. In that case, @@ -5161,6 +6230,7 @@ class basic_json // do the actual serialization j.dump(o, pretty_print, static_cast(indentation)); + return o; } @@ -5184,10 +6254,16 @@ class basic_json /// @{ /*! - @brief deserialize from string + @brief deserialize from an array - @param[in] s string to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t + This function reads from an array of 1-byte values. + + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @@ -5199,17 +6275,54 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. - @liveexample{The example below demonstrates the parse function with and - without callback function.,parse__string__parser_callback_t} + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} - @sa @ref parse(std::istream&, parser_callback_t) for a version that reads - from an input stream - - @since version 1.0.0 + @since version 2.0.3 */ - static basic_json parse(const string_t& s, parser_callback_t cb = nullptr) + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) { - return parser(s, cb).parse(); + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + + /*! + @brief deserialize from string literal + + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @sa @ref parse(std::istream&, const parser_callback_t) for a version that + reads from an input stream + + @since version 1.0.0 (originally for @ref string_t) + */ + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> + static basic_json parse(const CharT s, + const parser_callback_t cb = nullptr) + { + return parser(reinterpret_cast(s), cb).parse(); } /*! @@ -5228,27 +6341,153 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. - @liveexample{The example below demonstrates the parse function with and - without callback function.,parse__istream__parser_callback_t} + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} - @sa @ref parse(const string_t&, parser_callback_t) for a version that reads - from a string + @sa @ref parse(const CharT, const parser_callback_t) for a version + that reads from a string @since version 1.0.0 */ - static basic_json parse(std::istream& i, parser_callback_t cb = nullptr) + static basic_json parse(std::istream& i, + const parser_callback_t cb = nullptr) { return parser(i, cb).parse(); } /*! - @copydoc parse(std::istream&, parser_callback_t) + @copydoc parse(std::istream&, const parser_callback_t) */ - static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr) + static basic_json parse(std::istream&& i, + const parser_callback_t cb = nullptr) { return parser(i, cb).parse(); } + /*! + @brief deserialize from an iterator range with contiguous storage + + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. + + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam IteratorType iterator of container with contiguous storage + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} + + @since version 2.0.3 + */ + template::iterator_category>::value, int>::type = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr) + { + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate(first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } + + return parser(first, last, cb).parse(); + } + + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. + + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam ContiguousContainer container type with contiguous storage + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 + */ + template::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + /*! @brief deserialize from stream @@ -5267,8 +6506,8 @@ class basic_json @liveexample{The example below shows how a JSON value is constructed by reading a serialization from a stream.,operator_deserialize} - @sa parse(std::istream&, parser_callback_t) for a variant with a parser - callback function to filter values while parsing + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing @since version 1.0.0 */ @@ -5290,34 +6529,1563 @@ class basic_json /// @} + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ private: + /*! + @note Some code in the switch cases has been copied, because otherwise + copilers would complain about implicit fallthrough and there is no + portable attribute to mute such warnings. + */ + template + static void add_to_vector(std::vector& vec, size_t bytes, const T number) + { + assert(bytes == 1 or bytes == 2 or bytes == 4 or bytes == 8); + + switch (bytes) + { + case 8: + { + vec.push_back(static_cast((static_cast(number) >> 070) & 0xff)); + vec.push_back(static_cast((static_cast(number) >> 060) & 0xff)); + vec.push_back(static_cast((static_cast(number) >> 050) & 0xff)); + vec.push_back(static_cast((static_cast(number) >> 040) & 0xff)); + vec.push_back(static_cast((number >> 030) & 0xff)); + vec.push_back(static_cast((number >> 020) & 0xff)); + vec.push_back(static_cast((number >> 010) & 0xff)); + vec.push_back(static_cast(number & 0xff)); + break; + } + + case 4: + { + vec.push_back(static_cast((number >> 030) & 0xff)); + vec.push_back(static_cast((number >> 020) & 0xff)); + vec.push_back(static_cast((number >> 010) & 0xff)); + vec.push_back(static_cast(number & 0xff)); + break; + } + + case 2: + { + vec.push_back(static_cast((number >> 010) & 0xff)); + vec.push_back(static_cast(number & 0xff)); + break; + } + + case 1: + { + vec.push_back(static_cast(number & 0xff)); + break; + } + } + } + + /*! + @brief take sufficient bytes from a vector to fill an integer variable + + In the context of binary serialization formats, we need to read several + bytes from a byte vector and combine them to multi-byte integral data + types. + + @param[in] vec byte vector to read from + @param[in] current_index the position in the vector after which to read + + @return the next sizeof(T) bytes from @a vec, in reverse order as T + + @tparam T the integral return type + + @throw std::out_of_range if there are less than sizeof(T)+1 bytes in the + vector @a vec to read + + In the for loop, the bytes from the vector are copied in reverse order into + the return value. In the figures below, let sizeof(T)=4 and `i` be the loop + variable. + + Precondition: + + vec: | | | a | b | c | d | T: | | | | | + ^ ^ ^ ^ + current_index i ptr sizeof(T) + + Postcondition: + + vec: | | | a | b | c | d | T: | d | c | b | a | + ^ ^ ^ + | i ptr + current_index + + @sa Code adapted from . + */ + template + static T get_from_vector(const std::vector& vec, const size_t current_index) + { + if (current_index + sizeof(T) + 1 > vec.size()) + { + JSON_THROW(std::out_of_range("cannot read " + std::to_string(sizeof(T)) + " bytes from vector")); + } + + T result; + auto* ptr = reinterpret_cast(&result); + for (size_t i = 0; i < sizeof(T); ++i) + { + *ptr++ = vec[current_index + sizeof(T) - i]; + } + return result; + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + This is a straightforward implementation of the MessagePack specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md + */ + static void to_msgpack_internal(const basic_json& j, std::vector& v) + { + switch (j.type()) + { + case value_t::null: + { + // nil + v.push_back(0xc0); + break; + } + + case value_t::boolean: + { + // true and false + v.push_back(j.m_value.boolean ? 0xc3 : 0xc2); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we + // used the code from the value_t::number_unsigned case + // here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + { + // uint 64 + v.push_back(0xcf); + add_to_vector(v, 8, j.m_value.number_unsigned); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + { + // int 8 + v.push_back(0xd0); + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + { + // int 16 + v.push_back(0xd1); + add_to_vector(v, 2, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + { + // int 32 + v.push_back(0xd2); + add_to_vector(v, 4, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= std::numeric_limits::min() and j.m_value.number_integer <= std::numeric_limits::max()) + { + // int 64 + v.push_back(0xd3); + add_to_vector(v, 8, j.m_value.number_integer); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= std::numeric_limits::max()) + { + // uint 64 + v.push_back(0xcf); + add_to_vector(v, 8, j.m_value.number_unsigned); + } + break; + } + + case value_t::number_float: + { + // float 64 + v.push_back(0xcb); + const auto* helper = reinterpret_cast(&(j.m_value.number_float)); + for (size_t i = 0; i < 8; ++i) + { + v.push_back(helper[7 - i]); + } + break; + } + + case value_t::string: + { + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + v.push_back(static_cast(0xa0 | N)); + } + else if (N <= 255) + { + // str 8 + v.push_back(0xd9); + add_to_vector(v, 1, N); + } + else if (N <= 65535) + { + // str 16 + v.push_back(0xda); + add_to_vector(v, 2, N); + } + else if (N <= 4294967295) + { + // str 32 + v.push_back(0xdb); + add_to_vector(v, 4, N); + } + + // append string + std::copy(j.m_value.string->begin(), j.m_value.string->end(), + std::back_inserter(v)); + break; + } + + case value_t::array: + { + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + v.push_back(static_cast(0x90 | N)); + } + else if (N <= 0xffff) + { + // array 16 + v.push_back(0xdc); + add_to_vector(v, 2, N); + } + else if (N <= 0xffffffff) + { + // array 32 + v.push_back(0xdd); + add_to_vector(v, 4, N); + } + + // append each element + for (const auto& el : *j.m_value.array) + { + to_msgpack_internal(el, v); + } + break; + } + + case value_t::object: + { + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + v.push_back(static_cast(0x80 | (N & 0xf))); + } + else if (N <= 65535) + { + // map 16 + v.push_back(0xde); + add_to_vector(v, 2, N); + } + else if (N <= 4294967295) + { + // map 32 + v.push_back(0xdf); + add_to_vector(v, 4, N); + } + + // append each element + for (const auto& el : *j.m_value.object) + { + to_msgpack_internal(el.first, v); + to_msgpack_internal(el.second, v); + } + break; + } + + default: + { + break; + } + } + } + + /*! + @brief create a CBOR serialization of a given JSON value + + This is a straightforward implementation of the CBOR specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://tools.ietf.org/html/rfc7049 + */ + static void to_cbor_internal(const basic_json& j, std::vector& v) + { + switch (j.type()) + { + case value_t::null: + { + v.push_back(0xf6); + break; + } + + case value_t::boolean: + { + v.push_back(j.m_value.boolean ? 0xf5 : 0xf4); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer <= std::numeric_limits::max()) + { + v.push_back(0x18); + // one-byte uint8_t + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer <= std::numeric_limits::max()) + { + v.push_back(0x19); + // two-byte uint16_t + add_to_vector(v, 2, j.m_value.number_integer); + } + else if (j.m_value.number_integer <= std::numeric_limits::max()) + { + v.push_back(0x1a); + // four-byte uint32_t + add_to_vector(v, 4, j.m_value.number_integer); + } + else + { + v.push_back(0x1b); + // eight-byte uint64_t + add_to_vector(v, 8, j.m_value.number_integer); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + v.push_back(static_cast(0x20 + positive_number)); + } + else if (positive_number <= std::numeric_limits::max()) + { + // int 8 + v.push_back(0x38); + add_to_vector(v, 1, positive_number); + } + else if (positive_number <= std::numeric_limits::max()) + { + // int 16 + v.push_back(0x39); + add_to_vector(v, 2, positive_number); + } + else if (positive_number <= std::numeric_limits::max()) + { + // int 32 + v.push_back(0x3a); + add_to_vector(v, 4, positive_number); + } + else + { + // int 64 + v.push_back(0x3b); + add_to_vector(v, 8, positive_number); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + v.push_back(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= 0xff) + { + v.push_back(0x18); + // one-byte uint8_t + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= 0xffff) + { + v.push_back(0x19); + // two-byte uint16_t + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= 0xffffffff) + { + v.push_back(0x1a); + // four-byte uint32_t + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= 0xffffffffffffffff) + { + v.push_back(0x1b); + // eight-byte uint64_t + add_to_vector(v, 8, j.m_value.number_unsigned); + } + break; + } + + case value_t::number_float: + { + // Double-Precision Float + v.push_back(0xfb); + const auto* helper = reinterpret_cast(&(j.m_value.number_float)); + for (size_t i = 0; i < 8; ++i) + { + v.push_back(helper[7 - i]); + } + break; + } + + case value_t::string: + { + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + v.push_back(0x60 + static_cast(N)); // 1 byte for string + size + } + else if (N <= 0xff) + { + v.push_back(0x78); // one-byte uint8_t for N + add_to_vector(v, 1, N); + } + else if (N <= 0xffff) + { + v.push_back(0x79); // two-byte uint16_t for N + add_to_vector(v, 2, N); + } + else if (N <= 0xffffffff) + { + v.push_back(0x7a); // four-byte uint32_t for N + add_to_vector(v, 4, N); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + v.push_back(0x7b); // eight-byte uint64_t for N + add_to_vector(v, 8, N); + } + // LCOV_EXCL_STOP + + // append string + std::copy(j.m_value.string->begin(), j.m_value.string->end(), + std::back_inserter(v)); + break; + } + + case value_t::array: + { + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + v.push_back(0x80 + static_cast(N)); // 1 byte for array + size + } + else if (N <= 0xff) + { + v.push_back(0x98); // one-byte uint8_t for N + add_to_vector(v, 1, N); + } + else if (N <= 0xffff) + { + v.push_back(0x99); // two-byte uint16_t for N + add_to_vector(v, 2, N); + } + else if (N <= 0xffffffff) + { + v.push_back(0x9a); // four-byte uint32_t for N + add_to_vector(v, 4, N); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + v.push_back(0x9b); // eight-byte uint64_t for N + add_to_vector(v, 8, N); + } + // LCOV_EXCL_STOP + + // append each element + for (const auto& el : *j.m_value.array) + { + to_cbor_internal(el, v); + } + break; + } + + case value_t::object: + { + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + v.push_back(0xa0 + static_cast(N)); // 1 byte for object + size + } + else if (N <= 0xff) + { + v.push_back(0xb8); + add_to_vector(v, 1, N); // one-byte uint8_t for N + } + else if (N <= 0xffff) + { + v.push_back(0xb9); + add_to_vector(v, 2, N); // two-byte uint16_t for N + } + else if (N <= 0xffffffff) + { + v.push_back(0xba); + add_to_vector(v, 4, N); // four-byte uint32_t for N + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + v.push_back(0xbb); + add_to_vector(v, 8, N); // eight-byte uint64_t for N + } + // LCOV_EXCL_STOP + + // append each element + for (const auto& el : *j.m_value.object) + { + to_cbor_internal(el.first, v); + to_cbor_internal(el.second, v); + } + break; + } + + default: + { + break; + } + } + } + + + /* + @brief checks if given lengths do not exceed the size of a given vector + + To secure the access to the byte vector during CBOR/MessagePack + deserialization, bytes are copied from the vector into buffers. This + function checks if the number of bytes to copy (@a len) does not exceed + the size @s size of the vector. Additionally, an @a offset is given from + where to start reading the bytes. + + This function checks whether reading the bytes is safe; that is, offset is + a valid index in the vector, offset+len + + @param[in] size size of the byte vector + @param[in] len number of bytes to read + @param[in] offset offset where to start reading + + vec: x x x x x X X X X X + ^ ^ ^ + 0 offset len + + @throws out_of_range if `len > v.size()` + */ + static void check_length(const size_t size, const size_t len, const size_t offset) + { + // simple case: requested length is greater than the vector's length + if (len > size or offset > size) + { + JSON_THROW(std::out_of_range("len out of range")); + } + + // second case: adding offset would result in overflow + if ((size > (std::numeric_limits::max() - offset))) + { + JSON_THROW(std::out_of_range("len+offset out of range")); + } + + // last case: reading past the end of the vector + if (len + offset > size) + { + JSON_THROW(std::out_of_range("len+offset out of range")); + } + } + + /*! + @brief create a JSON value from a given MessagePack vector + + @param[in] v MessagePack serialization + @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md + */ + static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) + { + // make sure reading 1 byte is safe + check_length(v.size(), 1, idx); + + // store and increment index + const size_t current_idx = idx++; + + if (v[current_idx] <= 0xbf) + { + if (v[current_idx] <= 0x7f) // positive fixint + { + return v[current_idx]; + } + if (v[current_idx] <= 0x8f) // fixmap + { + basic_json result = value_t::object; + const size_t len = v[current_idx] & 0x0f; + for (size_t i = 0; i < len; ++i) + { + std::string key = from_msgpack_internal(v, idx); + result[key] = from_msgpack_internal(v, idx); + } + return result; + } + else if (v[current_idx] <= 0x9f) // fixarray + { + basic_json result = value_t::array; + const size_t len = v[current_idx] & 0x0f; + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_msgpack_internal(v, idx)); + } + return result; + } + else // fixstr + { + const size_t len = v[current_idx] & 0x1f; + const size_t offset = current_idx + 1; + idx += len; // skip content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + } + else if (v[current_idx] >= 0xe0) // negative fixint + { + return static_cast(v[current_idx]); + } + else + { + switch (v[current_idx]) + { + case 0xc0: // nil + { + return value_t::null; + } + + case 0xc2: // false + { + return false; + } + + case 0xc3: // true + { + return true; + } + + case 0xca: // float 32 + { + // copy bytes in reverse order into the double variable + float res; + for (size_t byte = 0; byte < sizeof(float); ++byte) + { + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); + } + idx += sizeof(float); // skip content bytes + return res; + } + + case 0xcb: // float 64 + { + // copy bytes in reverse order into the double variable + double res; + for (size_t byte = 0; byte < sizeof(double); ++byte) + { + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); + } + idx += sizeof(double); // skip content bytes + return res; + } + + case 0xcc: // uint 8 + { + idx += 1; // skip content byte + return get_from_vector(v, current_idx); + } + + case 0xcd: // uint 16 + { + idx += 2; // skip 2 content bytes + return get_from_vector(v, current_idx); + } + + case 0xce: // uint 32 + { + idx += 4; // skip 4 content bytes + return get_from_vector(v, current_idx); + } + + case 0xcf: // uint 64 + { + idx += 8; // skip 8 content bytes + return get_from_vector(v, current_idx); + } + + case 0xd0: // int 8 + { + idx += 1; // skip content byte + return get_from_vector(v, current_idx); + } + + case 0xd1: // int 16 + { + idx += 2; // skip 2 content bytes + return get_from_vector(v, current_idx); + } + + case 0xd2: // int 32 + { + idx += 4; // skip 4 content bytes + return get_from_vector(v, current_idx); + } + + case 0xd3: // int 64 + { + idx += 8; // skip 8 content bytes + return get_from_vector(v, current_idx); + } + + case 0xd9: // str 8 + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 2; + idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0xda: // str 16 + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 3; + idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0xdb: // str 32 + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 5; + idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0xdc: // array 16 + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 2; // skip 2 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_msgpack_internal(v, idx)); + } + return result; + } + + case 0xdd: // array 32 + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_msgpack_internal(v, idx)); + } + return result; + } + + case 0xde: // map 16 + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 2; // skip 2 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_msgpack_internal(v, idx); + result[key] = from_msgpack_internal(v, idx); + } + return result; + } + + case 0xdf: // map 32 + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_msgpack_internal(v, idx); + result[key] = from_msgpack_internal(v, idx); + } + return result; + } + + default: + { + JSON_THROW(std::invalid_argument("error parsing a msgpack @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx])))); + } + } + } + } + + /*! + @brief create a JSON value from a given CBOR vector + + @param[in] v CBOR serialization + @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid CBOR + @throw std::out_of_range if the given vector ends prematurely + + @sa https://tools.ietf.org/html/rfc7049 + */ + static basic_json from_cbor_internal(const std::vector& v, size_t& idx) + { + // store and increment index + const size_t current_idx = idx++; + + switch (v.at(current_idx)) + { + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + { + return v[current_idx]; + } + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + idx += 1; // skip content byte + return get_from_vector(v, current_idx); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + idx += 2; // skip 2 content bytes + return get_from_vector(v, current_idx); + } + + case 0x1a: // Unsigned integer (four-byte uint32_t follows) + { + idx += 4; // skip 4 content bytes + return get_from_vector(v, current_idx); + } + + case 0x1b: // Unsigned integer (eight-byte uint64_t follows) + { + idx += 8; // skip 8 content bytes + return get_from_vector(v, current_idx); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + { + return static_cast(0x20 - 1 - v[current_idx]); + } + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + idx += 1; // skip content byte + // must be uint8_t ! + return static_cast(-1) - get_from_vector(v, current_idx); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + idx += 2; // skip 2 content bytes + return static_cast(-1) - get_from_vector(v, current_idx); + } + + case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) + { + idx += 4; // skip 4 content bytes + return static_cast(-1) - get_from_vector(v, current_idx); + } + + case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) + { + idx += 8; // skip 8 content bytes + return static_cast(-1) - static_cast(get_from_vector(v, current_idx)); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + const auto len = static_cast(v[current_idx] - 0x60); + const size_t offset = current_idx + 1; + idx += len; // skip content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 2; + idx += len + 1; // skip size byte + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 3; + idx += len + 2; // skip 2 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 5; + idx += len + 4; // skip 4 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + { + const auto len = static_cast(get_from_vector(v, current_idx)); + const size_t offset = current_idx + 9; + idx += len + 8; // skip 8 size bytes + content bytes + check_length(v.size(), len, offset); + return std::string(reinterpret_cast(v.data()) + offset, len); + } + + case 0x7f: // UTF-8 string (indefinite length) + { + std::string result; + while (v.at(idx) != 0xff) + { + string_t s = from_cbor_internal(v, idx); + result += s; + } + // skip break byte (0xFF) + idx += 1; + return result; + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + { + basic_json result = value_t::array; + const auto len = static_cast(v[current_idx] - 0x80); + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x98: // array (one-byte uint8_t for n follows) + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 1; // skip 1 size byte + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 2; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x9a: // array (four-byte uint32_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x9b: // array (eight-byte uint64_t for n follow) + { + basic_json result = value_t::array; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 8; // skip 8 size bytes + for (size_t i = 0; i < len; ++i) + { + result.push_back(from_cbor_internal(v, idx)); + } + return result; + } + + case 0x9f: // array (indefinite length) + { + basic_json result = value_t::array; + while (v.at(idx) != 0xff) + { + result.push_back(from_cbor_internal(v, idx)); + } + // skip break byte (0xFF) + idx += 1; + return result; + } + + // map (0x00..0x17 pairs of data items follow) + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + { + basic_json result = value_t::object; + const auto len = static_cast(v[current_idx] - 0xa0); + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xb8: // map (one-byte uint8_t for n follows) + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 1; // skip 1 size byte + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xb9: // map (two-byte uint16_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 2; // skip 2 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xba: // map (four-byte uint32_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 4; // skip 4 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xbb: // map (eight-byte uint64_t for n follow) + { + basic_json result = value_t::object; + const auto len = static_cast(get_from_vector(v, current_idx)); + idx += 8; // skip 8 size bytes + for (size_t i = 0; i < len; ++i) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + return result; + } + + case 0xbf: // map (indefinite length) + { + basic_json result = value_t::object; + while (v.at(idx) != 0xff) + { + std::string key = from_cbor_internal(v, idx); + result[key] = from_cbor_internal(v, idx); + } + // skip break byte (0xFF) + idx += 1; + return result; + } + + case 0xf4: // false + { + return false; + } + + case 0xf5: // true + { + return true; + } + + case 0xf6: // null + { + return value_t::null; + } + + case 0xf9: // Half-Precision Float (two-byte IEEE 754) + { + idx += 2; // skip two content bytes + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added to + // IEEE 754 in 2008, today's programming platforms often still + // only have limited support for them. It is very easy to + // include at least decoding support for them even without such + // support. An example of a small decoder for half-precision + // floating-point numbers in the C language is shown in Fig. 3. + const int half = (v.at(current_idx + 1) << 8) + v.at(current_idx + 2); + const int exp = (half >> 10) & 0x1f; + const int mant = half & 0x3ff; + double val; + if (exp == 0) + { + val = std::ldexp(mant, -24); + } + else if (exp != 31) + { + val = std::ldexp(mant + 1024, exp - 25); + } + else + { + val = mant == 0 + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + } + return (half & 0x8000) != 0 ? -val : val; + } + + case 0xfa: // Single-Precision Float (four-byte IEEE 754) + { + // copy bytes in reverse order into the float variable + float res; + for (size_t byte = 0; byte < sizeof(float); ++byte) + { + reinterpret_cast(&res)[sizeof(float) - byte - 1] = v.at(current_idx + 1 + byte); + } + idx += sizeof(float); // skip content bytes + return res; + } + + case 0xfb: // Double-Precision Float (eight-byte IEEE 754) + { + // copy bytes in reverse order into the double variable + double res; + for (size_t byte = 0; byte < sizeof(double); ++byte) + { + reinterpret_cast(&res)[sizeof(double) - byte - 1] = v.at(current_idx + 1 + byte); + } + idx += sizeof(double); // skip content bytes + return res; + } + + default: // anything else (0xFF is handled inside the other types) + { + JSON_THROW(std::invalid_argument("error parsing a CBOR @ " + std::to_string(current_idx) + ": " + std::to_string(static_cast(v[current_idx])))); + } + } + } + + public: + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa @ref from_msgpack(const std::vector&, const size_t) for the + analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + + @since version 2.0.9 + */ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack_internal(j, result); + return result; + } + + /*! + @brief create a JSON value from a byte vector in MessagePack format + + Deserializes a given byte vector @a v to a JSON value using the MessagePack + serialization format. + + @param[in] v a byte vector in MessagePack format + @param[in] start_index the index to start reading from @a v (0 by default) + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(const std::vector&, const size_t) for the + related CBOR format + + @since version 2.0.9, parameter @a start_index since 2.1.1 + */ + static basic_json from_msgpack(const std::vector& v, + const size_t start_index = 0) + { + size_t i = start_index; + return from_msgpack_internal(v, i); + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa @ref from_cbor(const std::vector&, const size_t) for the + analogous deserialization + @sa @ref to_msgpack(const basic_json& for the related MessagePack format + + @since version 2.0.9 + */ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor_internal(j, result); + return result; + } + + /*! + @brief create a JSON value from a byte vector in CBOR format + + Deserializes a given byte vector @a v to a JSON value using the CBOR + (Concise Binary Object Representation) serialization format. + + @param[in] v a byte vector in CBOR format + @param[in] start_index the index to start reading from @a v (0 by default) + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(const std::vector&, const size_t) for the + related MessagePack format + + @since version 2.0.9, parameter @a start_index since 2.1.1 + */ + static basic_json from_cbor(const std::vector& v, + const size_t start_index = 0) + { + size_t i = start_index; + return from_cbor_internal(v, i); + } + + /// @} + /////////////////////////// // convenience functions // /////////////////////////// - /// return the type as string - string_t type_name() const + /*! + @brief return the type as string + + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. + + @return basically a string representation of a the @a m_type member + + @complexity Constant. + + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} + + @since version 1.0.0, public since 2.1.0 + */ + std::string type_name() const { - switch (m_type) { - case value_t::null: - return "null"; - case value_t::object: - return "object"; - case value_t::array: - return "array"; - case value_t::string: - return "string"; - case value_t::boolean: - return "boolean"; - case value_t::discarded: - return "discarded"; - default: - return "number"; + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::discarded: + return "discarded"; + default: + return "number"; + } } } + private: /*! @brief calculates the extra space to escape a JSON string @@ -5328,9 +8096,8 @@ class basic_json */ static std::size_t extra_space(const string_t& s) noexcept { - std::size_t result = 0; - - for (const auto& c : s) + return std::accumulate(s.begin(), s.end(), size_t{}, + [](size_t res, typename string_t::value_type c) { switch (c) { @@ -5343,8 +8110,7 @@ class basic_json case '\t': { // from c (1 byte) to \x (2 bytes) - result += 1; - break; + return res + 1; } default: @@ -5352,21 +8118,20 @@ class basic_json if (c >= 0x00 and c <= 0x1f) { // from c (1 byte) to \uxxxx (6 bytes) - result += 5; + return res + 5; } - break; + + return res; } } - } - - return result; + }); } /*! @brief escape a string - Escape a string by replacing certain special characters by a sequence of an - escape character (backslash) and another character and other control + Escape a string by replacing certain special characters by a sequence of + an escape character (backslash) and another character and other control characters by a sequence of "\u" followed by a four-digit hex representation. @@ -5375,7 +8140,7 @@ class basic_json @complexity Linear in the length of string @a s. */ - static string_t escape_string(const string_t& s) noexcept + static string_t escape_string(const string_t& s) { const auto space = extra_space(s); if (space == 0) @@ -5451,15 +8216,17 @@ class basic_json { if (c >= 0x00 and c <= 0x1f) { - // convert a number 0..15 to its hex representation (0..f) - auto hexify = [](const char v) -> char + // convert a number 0..15 to its hex representation + // (0..f) + static const char hexify[16] = { - return (v < 10) ? ('0' + v) : ('a' + v - 10); + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; // print character c as \uxxxx for (const char m : - { 'u', '0', '0', hexify(c >> 4), hexify(c & 0x0f) + { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f] }) { result[++pos] = m; @@ -5480,17 +8247,165 @@ class basic_json return result; } + + /*! + @brief locale-independent serialization for built-in arithmetic types + */ + struct numtostr + { + public: + template + numtostr(NumberType value) + { + x_write(value, std::is_integral()); + } + + const char* c_str() const + { + return m_buf.data(); + } + + private: + /// a (hopefully) large enough character buffer + std::array < char, 64 > m_buf{{}}; + + template + void x_write(NumberType x, /*is_integral=*/std::true_type) + { + // special case for "0" + if (x == 0) + { + m_buf[0] = '0'; + return; + } + + const bool is_negative = x < 0; + size_t i = 0; + + // spare 1 byte for '\0' + while (x != 0 and i < m_buf.size() - 1) + { + const auto digit = std::labs(static_cast(x % 10)); + m_buf[i++] = static_cast('0' + digit); + x /= 10; + } + + // make sure the number has been processed completely + assert(x == 0); + + if (is_negative) + { + // make sure there is capacity for the '-' + assert(i < m_buf.size() - 2); + m_buf[i++] = '-'; + } + + std::reverse(m_buf.begin(), m_buf.begin() + i); + } + + template + void x_write(NumberType x, /*is_integral=*/std::false_type) + { + // special case for 0.0 and -0.0 + if (x == 0) + { + size_t i = 0; + if (std::signbit(x)) + { + m_buf[i++] = '-'; + } + m_buf[i++] = '0'; + m_buf[i++] = '.'; + m_buf[i] = '0'; + return; + } + + // get number of digits for a text -> float -> text round-trip + static constexpr auto d = std::numeric_limits::digits10; + + // the actual conversion + const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x); + + // negative value indicates an error + assert(written_bytes > 0); + // check if buffer was large enough + assert(static_cast(written_bytes) < m_buf.size()); + + // read information from locale + const auto loc = localeconv(); + assert(loc != nullptr); + const char thousands_sep = !loc->thousands_sep ? '\0' + : loc->thousands_sep[0]; + + const char decimal_point = !loc->decimal_point ? '\0' + : loc->decimal_point[0]; + + // erase thousands separator + if (thousands_sep != '\0') + { + const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep); + std::fill(end, m_buf.end(), '\0'); + } + + // convert decimal point to '.' + if (decimal_point != '\0' and decimal_point != '.') + { + for (auto& c : m_buf) + { + if (c == decimal_point) + { + c = '.'; + break; + } + } + } + + // determine if need to append ".0" + size_t i = 0; + bool value_is_int_like = true; + for (i = 0; i < m_buf.size(); ++i) + { + // break when end of number is reached + if (m_buf[i] == '\0') + { + break; + } + + // check if we find non-int character + value_is_int_like = value_is_int_like and m_buf[i] != '.' and + m_buf[i] != 'e' and m_buf[i] != 'E'; + } + + if (value_is_int_like) + { + // there must be 2 bytes left for ".0" + assert((i + 2) < m_buf.size()); + // we write to the end of the number + assert(m_buf[i] == '\0'); + assert(m_buf[i - 1] != '\0'); + + // add ".0" + m_buf[i] = '.'; + m_buf[i + 1] = '0'; + + // the resulting string is properly terminated + assert(m_buf[i + 2] == '\0'); + } + } + }; + + /*! @brief internal implementation of the serialization function This function is called by the public member function dump and organizes the serialization internally. The indentation level is propagated as - additional parameter. In case of arrays and objects, the function is called - recursively. Note that + additional parameter. In case of arrays and objects, the function is + called recursively. Note that - - strings and object keys are escaped using escape_string() - - integer numbers are converted implicitly via operator<< - - floating-point numbers are converted to a string using "%g" format + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format @param[out] o stream to write to @param[in] pretty_print whether the output shall be pretty-printed @@ -5509,8 +8424,6 @@ class basic_json { case value_t::object: { - assert(m_value.object != nullptr); - if (m_value.object->empty()) { o << "{}"; @@ -5551,8 +8464,6 @@ class basic_json case value_t::array: { - assert(m_value.array != nullptr); - if (m_value.array->empty()) { o << "[]"; @@ -5591,7 +8502,6 @@ class basic_json case value_t::string: { - assert(m_value.string != nullptr); o << string_t("\"") << escape_string(*m_value.string) << "\""; return; } @@ -5604,29 +8514,19 @@ class basic_json case value_t::number_integer: { - o << m_value.number_integer; + o << numtostr(m_value.number_integer).c_str(); + return; + } + + case value_t::number_unsigned: + { + o << numtostr(m_value.number_unsigned).c_str(); return; } case value_t::number_float: { - // If the number is an integer then output as a fixed with with - // precision 1 to output "0.0", "1.0" etc as expected for some - // round trip tests otherwise 15 digits of precision allows - // round-trip IEEE 754 string->double->string; to be safe, we - // read this value from - // std::numeric_limits::digits10 - if (std::fmod(m_value.number_float, 1) == 0) - { - o << std::fixed << std::setprecision(1); - } - else - { - // std::defaultfloat not supported in gcc version < 5 - o.unsetf(std::ios_base::floatfield); - o << std::setprecision(std::numeric_limits::digits10); - } - o << m_value.number_float; + o << numtostr(m_value.number_float).c_str(); return; } @@ -5673,40 +8573,118 @@ class basic_json class primitive_iterator_t { public: + + difference_type get_value() const noexcept + { + return m_it; + } /// set iterator to a defined beginning - void set_begin() + void set_begin() noexcept { m_it = begin_value; } /// set iterator to a defined past the end - void set_end() + void set_end() noexcept { m_it = end_value; } /// return whether the iterator can be dereferenced - bool is_begin() const + constexpr bool is_begin() const noexcept { return (m_it == begin_value); } /// return whether the iterator is at end - bool is_end() const + constexpr bool is_end() const noexcept { return (m_it == end_value); } - /// return reference to the value to change and compare - operator difference_type& () + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { - return m_it; + return lhs.m_it == rhs.m_it; } - /// return value to compare - operator difference_type () const + friend constexpr bool operator!=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept { - return m_it; + return !(lhs == rhs); + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + friend constexpr bool operator<=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it <= rhs.m_it; + } + + friend constexpr bool operator>(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it > rhs.m_it; + } + + friend constexpr bool operator>=(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it >= rhs.m_it; + } + + primitive_iterator_t operator+(difference_type i) + { + auto result = *this; + result += i; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it) + { + return os << it.m_it; + } + + primitive_iterator_t& operator++() + { + ++m_it; + return *this; + } + + primitive_iterator_t operator++(int) + { + auto result = *this; + m_it++; + return result; + } + + primitive_iterator_t& operator--() + { + --m_it; + return *this; + } + + primitive_iterator_t operator--(int) + { + auto result = *this; + m_it--; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) + { + m_it -= n; + return *this; } private: @@ -5734,7 +8712,7 @@ class basic_json primitive_iterator_t primitive_iterator; /// create an uninitialized internal_iterator - internal_iterator() + internal_iterator() noexcept : object_iterator(), array_iterator(), primitive_iterator() {} }; @@ -5754,7 +8732,7 @@ class basic_json size_t array_index = 0; public: - iteration_proxy_internal(IteratorType it) + explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {} @@ -5818,18 +8796,18 @@ class basic_json public: /// construct iteration proxy from a container - iteration_proxy(typename IteratorType::reference cont) + explicit iteration_proxy(typename IteratorType::reference cont) : container(cont) {} /// return iterator begin (needed for range-based for) - iteration_proxy_internal begin() + iteration_proxy_internal begin() noexcept { return iteration_proxy_internal(container.begin()); } /// return iterator end (needed for range-based for) - iteration_proxy_internal end() + iteration_proxy_internal end() noexcept { return iteration_proxy_internal(container.end()); } @@ -5837,40 +8815,62 @@ class basic_json public: /*! - @brief a const random access iterator for the @ref basic_json class + @brief a template for a random access iterator for the @ref basic_json class - This class implements a const iterator for the @ref basic_json class. From - this class, the @ref iterator class is derived. + This class implements a both iterators (iterator and const_iterator) for the + @ref basic_json class. + + @note An iterator is called *initialized* when a pointer to a JSON value + has been set (e.g., by a constructor or a copy assignment). If the + iterator is default-constructed, it is *uninitialized* and most + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): The iterator that can be moved to point (forward and backward) to any element in constant time. - @since version 1.0.0 + @since version 1.0.0, simplified in version 2.0.9 */ - class const_iterator : public std::iterator + template + class iter_impl : public std::iterator { /// allow basic_json to access private members friend class basic_json; + // make sure U is basic_json or const basic_json + static_assert(std::is_same::value + or std::is_same::value, + "iter_impl only accepts (const) basic_json"); + public: /// the type of the values when the iterator is dereferenced using value_type = typename basic_json::value_type; /// a type to represent differences between iterators using difference_type = typename basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) - using pointer = typename basic_json::const_pointer; + using pointer = typename std::conditional::value, + typename basic_json::const_pointer, + typename basic_json::pointer>::type; /// defines a reference to the type iterated over (value_type) - using reference = typename basic_json::const_reference; + using reference = typename std::conditional::value, + typename basic_json::const_reference, + typename basic_json::reference>::type; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// default constructor - const_iterator() = default; + iter_impl() = default; - /// constructor for a given JSON instance - const_iterator(pointer object) : m_object(object) + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept + : m_object(object) { assert(m_object != nullptr); @@ -5896,40 +8896,42 @@ class basic_json } } - /// copy constructor given a nonconst iterator - const_iterator(const iterator& other) : m_object(other.m_object) + /* + Use operator `const_iterator` instead of `const_iterator(const iterator& + other) noexcept` to avoid two class definitions for @ref iterator and + @ref const_iterator. + + This function is only called if this class is an @ref iterator. If this + class is a @ref const_iterator this function is not called. + */ + operator const_iterator() const { - assert(m_object != nullptr); + const_iterator ret; - switch (m_object->m_type) + if (m_object) { - case basic_json::value_t::object: - { - m_it.object_iterator = other.m_it.object_iterator; - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } - - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; - } + ret.m_object = m_object; + ret.m_it = m_it; } + + return ret; } - /// copy constructor - const_iterator(const const_iterator& other) noexcept + /*! + @brief copy constructor + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} - /// copy assignment - const_iterator& operator=(const_iterator other) noexcept( + /*! + @brief copy assignment + @param[in,out] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(iter_impl other) noexcept( std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value and std::is_nothrow_move_constructible::value and @@ -5942,8 +8944,11 @@ class basic_json } private: - /// set the iterator to the first value - void set_begin() + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept { assert(m_object != nullptr); @@ -5951,14 +8956,12 @@ class basic_json { case basic_json::value_t::object: { - assert(m_object->m_value.object != nullptr); m_it.object_iterator = m_object->m_value.object->begin(); break; } case basic_json::value_t::array: { - assert(m_object->m_value.array != nullptr); m_it.array_iterator = m_object->m_value.array->begin(); break; } @@ -5978,8 +8981,11 @@ class basic_json } } - /// set the iterator past the last value - void set_end() + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept { assert(m_object != nullptr); @@ -5987,14 +8993,12 @@ class basic_json { case basic_json::value_t::object: { - assert(m_object->m_value.object != nullptr); m_it.object_iterator = m_object->m_value.object->end(); break; } case basic_json::value_t::array: { - assert(m_object->m_value.array != nullptr); m_it.array_iterator = m_object->m_value.array->end(); break; } @@ -6008,7 +9012,10 @@ class basic_json } public: - /// return a reference to the value pointed to by the iterator + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference operator*() const { assert(m_object != nullptr); @@ -6017,21 +9024,19 @@ class basic_json { case basic_json::value_t::object: { - assert(m_object->m_value.object); assert(m_it.object_iterator != m_object->m_value.object->end()); return m_it.object_iterator->second; } case basic_json::value_t::array: { - assert(m_object->m_value.array); assert(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } case basic_json::value_t::null: { - throw std::out_of_range("cannot get value"); + JSON_THROW(std::out_of_range("cannot get value")); } default: @@ -6040,15 +9045,16 @@ class basic_json { return *m_object; } - else - { - throw std::out_of_range("cannot get value"); - } + + JSON_THROW(std::out_of_range("cannot get value")); } } } - /// dereference the iterator + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ pointer operator->() const { assert(m_object != nullptr); @@ -6057,14 +9063,12 @@ class basic_json { case basic_json::value_t::object: { - assert(m_object->m_value.object); assert(m_it.object_iterator != m_object->m_value.object->end()); return &(m_it.object_iterator->second); } case basic_json::value_t::array: { - assert(m_object->m_value.array); assert(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } @@ -6075,24 +9079,28 @@ class basic_json { return m_object; } - else - { - throw std::out_of_range("cannot get value"); - } + + JSON_THROW(std::out_of_range("cannot get value")); } } } - /// post-increment (it++) - const_iterator operator++(int) + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator++(int) { auto result = *this; ++(*this); return result; } - /// pre-increment (++it) - const_iterator& operator++() + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() { assert(m_object != nullptr); @@ -6100,13 +9108,13 @@ class basic_json { case basic_json::value_t::object: { - ++m_it.object_iterator; + std::advance(m_it.object_iterator, 1); break; } case basic_json::value_t::array: { - ++m_it.array_iterator; + std::advance(m_it.array_iterator, 1); break; } @@ -6120,16 +9128,22 @@ class basic_json return *this; } - /// post-decrement (it--) - const_iterator operator--(int) + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator--(int) { auto result = *this; --(*this); return result; } - /// pre-decrement (--it) - const_iterator& operator--() + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() { assert(m_object != nullptr); @@ -6137,13 +9151,13 @@ class basic_json { case basic_json::value_t::object: { - --m_it.object_iterator; + std::advance(m_it.object_iterator, -1); break; } case basic_json::value_t::array: { - --m_it.array_iterator; + std::advance(m_it.array_iterator, -1); break; } @@ -6157,13 +9171,16 @@ class basic_json return *this; } - /// comparison: equal - bool operator==(const const_iterator& other) const + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - throw std::domain_error("cannot compare iterators of different containers"); + JSON_THROW(std::domain_error("cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -6187,19 +9204,25 @@ class basic_json } } - /// comparison: not equal - bool operator!=(const const_iterator& other) const + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const { return not operator==(other); } - /// comparison: smaller - bool operator<(const const_iterator& other) const + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined if (m_object != other.m_object) { - throw std::domain_error("cannot compare iterators of different containers"); + JSON_THROW(std::domain_error("cannot compare iterators of different containers")); } assert(m_object != nullptr); @@ -6208,7 +9231,7 @@ class basic_json { case basic_json::value_t::object: { - throw std::domain_error("cannot compare order of object iterators"); + JSON_THROW(std::domain_error("cannot compare order of object iterators")); } case basic_json::value_t::array: @@ -6223,26 +9246,38 @@ class basic_json } } - /// comparison: less than or equal - bool operator<=(const const_iterator& other) const + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const { return not other.operator < (*this); } - /// comparison: greater than - bool operator>(const const_iterator& other) const + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const { return not operator<=(other); } - /// comparison: greater than or equal - bool operator>=(const const_iterator& other) const + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const { return not operator<(other); } - /// add to iterator - const_iterator& operator+=(difference_type i) + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) { assert(m_object != nullptr); @@ -6250,12 +9285,12 @@ class basic_json { case basic_json::value_t::object: { - throw std::domain_error("cannot use offsets with object iterators"); + JSON_THROW(std::domain_error("cannot use offsets with object iterators")); } case basic_json::value_t::array: { - m_it.array_iterator += i; + std::advance(m_it.array_iterator, i); break; } @@ -6269,30 +9304,42 @@ class basic_json return *this; } - /// subtract from iterator - const_iterator& operator-=(difference_type i) + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) { return operator+=(-i); } - /// add to iterator - const_iterator operator+(difference_type i) + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) { auto result = *this; result += i; return result; } - /// subtract from iterator - const_iterator operator-(difference_type i) + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) { auto result = *this; result -= i; return result; } - /// return difference - difference_type operator-(const const_iterator& other) const + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const { assert(m_object != nullptr); @@ -6300,7 +9347,7 @@ class basic_json { case basic_json::value_t::object: { - throw std::domain_error("cannot use offsets with object iterators"); + JSON_THROW(std::domain_error("cannot use offsets with object iterators")); } case basic_json::value_t::array: @@ -6315,7 +9362,10 @@ class basic_json } } - /// access to successor + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference operator[](difference_type n) const { assert(m_object != nullptr); @@ -6324,34 +9374,35 @@ class basic_json { case basic_json::value_t::object: { - throw std::domain_error("cannot use operator[] for object iterators"); + JSON_THROW(std::domain_error("cannot use operator[] for object iterators")); } case basic_json::value_t::array: { - return *(m_it.array_iterator + n); + return *std::next(m_it.array_iterator, n); } case basic_json::value_t::null: { - throw std::out_of_range("cannot get value"); + JSON_THROW(std::out_of_range("cannot get value")); } default: { - if (m_it.primitive_iterator == -n) + if (m_it.primitive_iterator.get_value() == -n) { return *m_object; } - else - { - throw std::out_of_range("cannot get value"); - } + + JSON_THROW(std::out_of_range("cannot get value")); } } } - /// return the key of an object iterator + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ typename object_t::key_type key() const { assert(m_object != nullptr); @@ -6360,13 +9411,14 @@ class basic_json { return m_it.object_iterator->first; } - else - { - throw std::domain_error("cannot use key() for non-object iterators"); - } + + JSON_THROW(std::domain_error("cannot use key() for non-object iterators")); } - /// return the value of an iterator + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference value() const { return operator*(); @@ -6379,140 +9431,6 @@ class basic_json internal_iterator m_it = internal_iterator(); }; - /*! - @brief a mutable random access iterator for the @ref basic_json class - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element. - - @since version 1.0.0 - */ - class iterator : public const_iterator - { - public: - using base_iterator = const_iterator; - using pointer = typename basic_json::pointer; - using reference = typename basic_json::reference; - - /// default constructor - iterator() = default; - - /// constructor for a given JSON instance - iterator(pointer object) noexcept - : base_iterator(object) - {} - - /// copy constructor - iterator(const iterator& other) noexcept - : base_iterator(other) - {} - - /// copy assignment - iterator& operator=(iterator other) noexcept( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - base_iterator::operator=(other); - return *this; - } - - /// return a reference to the value pointed to by the iterator - reference operator*() - { - return const_cast(base_iterator::operator*()); - } - - /// dereference the iterator - pointer operator->() - { - return const_cast(base_iterator::operator->()); - } - - /// post-increment (it++) - iterator operator++(int) - { - iterator result = *this; - base_iterator::operator++(); - return result; - } - - /// pre-increment (++it) - iterator& operator++() - { - base_iterator::operator++(); - return *this; - } - - /// post-decrement (it--) - iterator operator--(int) - { - iterator result = *this; - base_iterator::operator--(); - return result; - } - - /// pre-decrement (--it) - iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - - /// add to iterator - iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } - - /// subtract from iterator - iterator& operator-=(difference_type i) - { - base_iterator::operator-=(i); - return *this; - } - - /// add to iterator - iterator operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - iterator operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - difference_type operator-(const iterator& other) const - { - return base_iterator::operator-(other); - } - - /// access to successor - reference operator[](difference_type n) const - { - return const_cast(base_iterator::operator[](n)); - } - - /// return the value of an iterator - reference value() const - { - return const_cast(base_iterator::value()); - } - }; - /*! @brief a template for a reverse iterator class @@ -6540,12 +9458,12 @@ class basic_json using reference = typename Base::reference; /// create reverse iterator from iterator - json_reverse_iterator(const typename base_iterator::iterator_type& it) + json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept : base_iterator(it) {} /// create reverse iterator from base class - json_reverse_iterator(const base_iterator& it) + json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} @@ -6635,8 +9553,8 @@ class basic_json @brief lexical analysis This class organizes the lexical analysis during JSON deserialization. The - core of it is a scanner generated by re2c that processes - a buffer and recognizes tokens according to RFC 7159. + core of it is a scanner generated by [re2c](http://re2c.org) that + processes a buffer and recognizes tokens according to RFC 7159. */ class lexer { @@ -6644,75 +9562,90 @@ class basic_json /// token types for the parser enum class token_type { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the "true" literal - literal_false, ///< the "false" literal - literal_null, ///< the "null" literal - value_string, ///< a string -- use get_string() for actual value - value_number, ///< a number -- use get_number() for actual value - begin_array, ///< the character for array begin "[" - begin_object, ///< the character for object begin "{" - end_array, ///< the character for array end "]" - end_object, ///< the character for object end "}" - name_separator, ///< the name separator ":" - value_separator, ///< the value separator "," - parse_error, ///< indicating a parse error - end_of_input ///< indicating the end of the input buffer + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number() for actual value + value_integer, ///< a signed integer -- use get_number() for actual value + value_float, ///< an floating point number -- use get_number() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input ///< indicating the end of the input buffer }; /// the char type to use in the lexer using lexer_char_t = unsigned char; - /// constructor with a given buffer - explicit lexer(const string_t& s) noexcept - : m_stream(nullptr), m_buffer(s) + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_content(buff) { - m_content = reinterpret_cast(s.c_str()); assert(m_content != nullptr); m_start = m_cursor = m_content; - m_limit = m_content + s.size(); + m_limit = m_content + len; } - /// constructor with a given stream - explicit lexer(std::istream* s) noexcept - : m_stream(s), m_buffer() + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_line_buffer() { - assert(m_stream != nullptr); - getline(*m_stream, m_buffer); - m_content = reinterpret_cast(m_buffer.c_str()); - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + m_buffer.size(); + // immediately abort if stream is erroneous + if (s.fail()) + { + JSON_THROW(std::invalid_argument("stream error")); + } + + // fill buffer + fill_line_buffer(); + + // skip UTF-8 byte-order mark + if (m_line_buffer.size() >= 3 and m_line_buffer.substr(0, 3) == "\xEF\xBB\xBF") + { + m_line_buffer[0] = ' '; + m_line_buffer[1] = ' '; + m_line_buffer[2] = ' '; + } } - /// default constructor - lexer() = default; - - // switch off unwanted functions + // switch off unwanted functions (due to pointer members) + lexer() = delete; lexer(const lexer&) = delete; lexer operator=(const lexer&) = delete; /*! - @brief create a string from a Unicode code point + @brief create a string from one or two Unicode code points + + There are two cases: (1) @a codepoint1 is in the Basic Multilingual + Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2) + @a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to + represent a code point above U+FFFF. @param[in] codepoint1 the code point (can be high surrogate) @param[in] codepoint2 the code point (can be low surrogate or 0) - @return string representation of the code point + @return string representation of the code point; the length of the + result string is between 1 and 4 characters. - @throw std::out_of_range if code point is >0x10ffff; example: `"code + @throw std::out_of_range if code point is > 0x10ffff; example: `"code points above 0x10FFFF are invalid"` @throw std::invalid_argument if the low surrogate is invalid; example: `""missing or wrong low surrogate""` + @complexity Constant. + @see */ static string_t to_unicode(const std::size_t codepoint1, const std::size_t codepoint2 = 0) { - string_t result; - - // calculate the codepoint from the given code points + // calculate the code point from the given code points std::size_t codepoint = codepoint1; // check if codepoint1 is a high surrogate @@ -6733,10 +9666,12 @@ class basic_json } else { - throw std::invalid_argument("missing or wrong low surrogate"); + JSON_THROW(std::invalid_argument("missing or wrong low surrogate")); } } + string_t result; + if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) @@ -6765,14 +9700,14 @@ class basic_json } else { - throw std::out_of_range("code points above 0x10FFFF are invalid"); + JSON_THROW(std::out_of_range("code points above 0x10FFFF are invalid")); } return result; } /// return name of values of type token_type (only used for errors) - static std::string token_type_name(token_type t) + static std::string token_type_name(const token_type t) { switch (t) { @@ -6786,7 +9721,9 @@ class basic_json return "null literal"; case token_type::value_string: return "string literal"; - case token_type::value_number: + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: return "number literal"; case token_type::begin_array: return "'['"; @@ -6815,837 +9752,1163 @@ class basic_json /*! This function implements a scanner for JSON. It is specified using regular expressions that try to follow RFC 7159 as close as possible. - These regular expressions are then translated into a deterministic - finite automaton (DFA) by the tool re2c . As a result, - the translated code for this function consists of a large block of code - with goto jumps. + These regular expressions are then translated into a minimized + deterministic finite automaton (DFA) by the tool + [re2c](http://re2c.org). As a result, the translated code for this + function consists of a large block of code with `goto` jumps. @return the class of the next token read from the buffer + + @complexity Linear in the length of the input.\n + + Proposition: The loop below will always terminate for finite input.\n + + Proof (by contradiction): Assume a finite input. To loop forever, the + loop must never hit code with a `break` statement. The only code + snippets without a `break` statement are the continue statements for + whitespace and byte-order-marks. To loop forever, the input must be an + infinite sequence of whitespace or byte-order-marks. This contradicts + the assumption of finite input, q.e.d. */ - token_type scan() noexcept + token_type scan() { - // pointer for backtracking information - m_marker = nullptr; - - // remember the begin of the token - m_start = m_cursor; - assert(m_start != nullptr); - - + while (true) { - lexer_char_t yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = + // pointer for backtracking information + m_marker = nullptr; + + // remember the begin of the token + m_start = m_cursor; + assert(m_start != nullptr); + + { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 32, 32, 0, 0, 32, 0, 0, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 96, 64, 0, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 0, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - }; - if ((m_limit - m_cursor) < 5) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= ':') - { - if (yych <= ' ') + lexer_char_t yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = { - if (yych <= '\n') + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 32, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 160, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 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, + }; + if ((m_limit - m_cursor) < 5) + { + fill_line_buffer(5); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + if (yych <= '[') + { + if (yych <= '-') { - if (yych <= 0x00) + if (yych <= '"') { - goto basic_json_parser_28; + if (yych <= 0x00) + { + goto basic_json_parser_2; + } + if (yych <= '!') + { + goto basic_json_parser_4; + } + goto basic_json_parser_9; } - if (yych <= 0x08) + else { - goto basic_json_parser_30; - } - if (yych >= '\n') - { - goto basic_json_parser_4; + if (yych <= '+') + { + goto basic_json_parser_4; + } + if (yych <= ',') + { + goto basic_json_parser_10; + } + goto basic_json_parser_12; } } else { - if (yych == '\r') + if (yych <= '9') { - goto basic_json_parser_2; + if (yych <= '/') + { + goto basic_json_parser_4; + } + if (yych <= '0') + { + goto basic_json_parser_13; + } + goto basic_json_parser_15; } - if (yych <= 0x1F) + else { - goto basic_json_parser_30; + if (yych <= ':') + { + goto basic_json_parser_17; + } + if (yych <= 'Z') + { + goto basic_json_parser_4; + } + goto basic_json_parser_19; } } } else { - if (yych <= ',') + if (yych <= 'n') { - if (yych == '"') + if (yych <= 'e') { - goto basic_json_parser_27; + if (yych == ']') + { + goto basic_json_parser_21; + } + goto basic_json_parser_4; } - if (yych <= '+') + else { - goto basic_json_parser_30; - } - goto basic_json_parser_16; - } - else - { - if (yych <= '/') - { - if (yych <= '-') + if (yych <= 'f') { goto basic_json_parser_23; } - goto basic_json_parser_30; + if (yych <= 'm') + { + goto basic_json_parser_4; + } + goto basic_json_parser_24; + } + } + else + { + if (yych <= 'z') + { + if (yych == 't') + { + goto basic_json_parser_25; + } + goto basic_json_parser_4; } else { - if (yych <= '0') - { - goto basic_json_parser_24; - } - if (yych <= '9') + if (yych <= '{') { goto basic_json_parser_26; } - goto basic_json_parser_18; - } - } - } - } - else - { - if (yych <= 'n') - { - if (yych <= ']') - { - if (yych == '[') - { - goto basic_json_parser_8; - } - if (yych <= '\\') - { - goto basic_json_parser_30; - } - goto basic_json_parser_10; - } - else - { - if (yych == 'f') - { - goto basic_json_parser_22; - } - if (yych <= 'm') - { - goto basic_json_parser_30; - } - goto basic_json_parser_20; - } - } - else - { - if (yych <= '{') - { - if (yych == 't') - { - goto basic_json_parser_21; - } - if (yych <= 'z') - { - goto basic_json_parser_30; - } - goto basic_json_parser_12; - } - else - { - if (yych <= '}') - { - if (yych <= '|') + if (yych == '}') { - goto basic_json_parser_30; + goto basic_json_parser_28; } - goto basic_json_parser_14; - } - else - { - if (yych == 0xEF) - { - goto basic_json_parser_6; - } - goto basic_json_parser_30; + goto basic_json_parser_4; } } } - } basic_json_parser_2: - ++m_cursor; - yych = *m_cursor; - goto basic_json_parser_5; -basic_json_parser_3: - { - return scan(); - } + ++m_cursor; + { + last_token_type = token_type::end_of_input; + break; + } basic_json_parser_4: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; + ++m_cursor; basic_json_parser_5: - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_4; - } - goto basic_json_parser_3; + { + last_token_type = token_type::parse_error; + break; + } basic_json_parser_6: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 0xBB) - { - goto basic_json_parser_64; - } -basic_json_parser_7: - { - return token_type::parse_error; - } -basic_json_parser_8: - ++m_cursor; - { - return token_type::begin_array; - } + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 32) + { + goto basic_json_parser_6; + } + { + continue; + } +basic_json_parser_9: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych <= 0x1F) + { + goto basic_json_parser_5; + } + if (yych <= 0x7F) + { + goto basic_json_parser_31; + } + if (yych <= 0xC1) + { + goto basic_json_parser_5; + } + if (yych <= 0xF4) + { + goto basic_json_parser_31; + } + goto basic_json_parser_5; basic_json_parser_10: - ++m_cursor; - { - return token_type::end_array; - } + ++m_cursor; + { + last_token_type = token_type::value_separator; + break; + } basic_json_parser_12: - ++m_cursor; - { - return token_type::begin_object; - } -basic_json_parser_14: - ++m_cursor; - { - return token_type::end_object; - } -basic_json_parser_16: - ++m_cursor; - { - return token_type::value_separator; - } -basic_json_parser_18: - ++m_cursor; - { - return token_type::name_separator; - } -basic_json_parser_20: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'u') - { - goto basic_json_parser_60; - } - goto basic_json_parser_7; -basic_json_parser_21: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'r') - { - goto basic_json_parser_56; - } - goto basic_json_parser_7; -basic_json_parser_22: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'a') - { - goto basic_json_parser_51; - } - goto basic_json_parser_7; -basic_json_parser_23: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_7; - } - if (yych <= '0') - { - goto basic_json_parser_50; - } - if (yych <= '9') - { - goto basic_json_parser_41; - } - goto basic_json_parser_7; -basic_json_parser_24: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') - { - if (yych == '.') + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_5; + } + if (yych <= '0') { goto basic_json_parser_43; } - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - } -basic_json_parser_25: - { - return token_type::value_number; - } -basic_json_parser_26: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - goto basic_json_parser_42; -basic_json_parser_27: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych <= 0x0F) - { - goto basic_json_parser_7; - } - goto basic_json_parser_32; -basic_json_parser_28: - ++m_cursor; - { - return token_type::end_of_input; - } -basic_json_parser_30: - yych = *++m_cursor; - goto basic_json_parser_7; -basic_json_parser_31: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; -basic_json_parser_32: - if (yybm[0 + yych] & 64) - { - goto basic_json_parser_31; - } - if (yych <= 0x0F) - { - goto basic_json_parser_33; - } - if (yych <= '"') - { - goto basic_json_parser_35; - } - goto basic_json_parser_34; -basic_json_parser_33: - m_cursor = m_marker; - if (yyaccept == 0) - { - goto basic_json_parser_7; - } - else - { - goto basic_json_parser_25; - } -basic_json_parser_34: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= 'e') - { - if (yych <= '/') - { - if (yych == '"') - { - goto basic_json_parser_31; - } - if (yych <= '.') - { - goto basic_json_parser_33; - } - goto basic_json_parser_31; - } - else - { - if (yych <= '\\') - { - if (yych <= '[') - { - goto basic_json_parser_33; - } - goto basic_json_parser_31; - } - else - { - if (yych == 'b') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - } - } - else - { - if (yych <= 'q') - { - if (yych <= 'f') - { - goto basic_json_parser_31; - } - if (yych == 'n') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 's') - { - if (yych <= 'r') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 't') - { - goto basic_json_parser_31; - } - if (yych <= 'u') - { - goto basic_json_parser_37; - } - goto basic_json_parser_33; - } - } - } -basic_json_parser_35: - ++m_cursor; - { - return token_type::value_string; - } -basic_json_parser_37: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_38; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych >= 'g') - { - goto basic_json_parser_33; - } - } -basic_json_parser_38: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_39; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych >= 'g') - { - goto basic_json_parser_33; - } - } -basic_json_parser_39: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_40; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych >= 'g') - { - goto basic_json_parser_33; - } - } -basic_json_parser_40: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } if (yych <= '9') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_31; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych <= 'f') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } -basic_json_parser_41: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; -basic_json_parser_42: - if (yybm[0 + yych] & 128) - { - goto basic_json_parser_41; - } - if (yych <= 'D') - { - if (yych != '.') - { - goto basic_json_parser_25; - } - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_25; - } -basic_json_parser_43: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_48; - } - goto basic_json_parser_33; -basic_json_parser_44: - yych = *++m_cursor; - if (yych <= ',') - { - if (yych != '+') - { - goto basic_json_parser_33; - } - } - else - { - if (yych <= '-') { goto basic_json_parser_45; } + goto basic_json_parser_5; +basic_json_parser_13: + yyaccept = 1; + yych = *(m_marker = ++m_cursor); + if (yych <= '9') + { + if (yych == '.') + { + goto basic_json_parser_47; + } + if (yych >= '0') + { + goto basic_json_parser_48; + } + } + else + { + if (yych <= 'E') + { + if (yych >= 'E') + { + goto basic_json_parser_51; + } + } + else + { + if (yych == 'e') + { + goto basic_json_parser_51; + } + } + } +basic_json_parser_14: + { + last_token_type = token_type::value_unsigned; + break; + } +basic_json_parser_15: + yyaccept = 1; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(3); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yybm[0 + yych] & 64) + { + goto basic_json_parser_15; + } + if (yych <= 'D') + { + if (yych == '.') + { + goto basic_json_parser_47; + } + goto basic_json_parser_14; + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_51; + } + if (yych == 'e') + { + goto basic_json_parser_51; + } + goto basic_json_parser_14; + } +basic_json_parser_17: + ++m_cursor; + { + last_token_type = token_type::name_separator; + break; + } +basic_json_parser_19: + ++m_cursor; + { + last_token_type = token_type::begin_array; + break; + } +basic_json_parser_21: + ++m_cursor; + { + last_token_type = token_type::end_array; + break; + } +basic_json_parser_23: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'a') + { + goto basic_json_parser_52; + } + goto basic_json_parser_5; +basic_json_parser_24: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'u') + { + goto basic_json_parser_53; + } + goto basic_json_parser_5; +basic_json_parser_25: + yyaccept = 0; + yych = *(m_marker = ++m_cursor); + if (yych == 'r') + { + goto basic_json_parser_54; + } + goto basic_json_parser_5; +basic_json_parser_26: + ++m_cursor; + { + last_token_type = token_type::begin_object; + break; + } +basic_json_parser_28: + ++m_cursor; + { + last_token_type = token_type::end_object; + break; + } +basic_json_parser_30: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; +basic_json_parser_31: + if (yybm[0 + yych] & 128) + { + goto basic_json_parser_30; + } + if (yych <= 0xE0) + { + if (yych <= '\\') + { + if (yych <= 0x1F) + { + goto basic_json_parser_32; + } + if (yych <= '"') + { + goto basic_json_parser_33; + } + goto basic_json_parser_35; + } + else + { + if (yych <= 0xC1) + { + goto basic_json_parser_32; + } + if (yych <= 0xDF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_37; + } + } + else + { + if (yych <= 0xEF) + { + if (yych == 0xED) + { + goto basic_json_parser_39; + } + goto basic_json_parser_38; + } + else + { + if (yych <= 0xF0) + { + goto basic_json_parser_40; + } + if (yych <= 0xF3) + { + goto basic_json_parser_41; + } + if (yych <= 0xF4) + { + goto basic_json_parser_42; + } + } + } +basic_json_parser_32: + m_cursor = m_marker; + if (yyaccept <= 1) + { + if (yyaccept == 0) + { + goto basic_json_parser_5; + } + else + { + goto basic_json_parser_14; + } + } + else + { + if (yyaccept == 2) + { + goto basic_json_parser_44; + } + else + { + goto basic_json_parser_58; + } + } +basic_json_parser_33: + ++m_cursor; + { + last_token_type = token_type::value_string; + break; + } +basic_json_parser_35: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 'e') + { + if (yych <= '/') + { + if (yych == '"') + { + goto basic_json_parser_30; + } + if (yych <= '.') + { + goto basic_json_parser_32; + } + goto basic_json_parser_30; + } + else + { + if (yych <= '\\') + { + if (yych <= '[') + { + goto basic_json_parser_32; + } + goto basic_json_parser_30; + } + else + { + if (yych == 'b') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + } + } + else + { + if (yych <= 'q') + { + if (yych <= 'f') + { + goto basic_json_parser_30; + } + if (yych == 'n') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 's') + { + if (yych <= 'r') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 't') + { + goto basic_json_parser_30; + } + if (yych <= 'u') + { + goto basic_json_parser_55; + } + goto basic_json_parser_32; + } + } + } +basic_json_parser_36: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; +basic_json_parser_37: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x9F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_38: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_39: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x9F) + { + goto basic_json_parser_36; + } + goto basic_json_parser_32; +basic_json_parser_40: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x8F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_41: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0xBF) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_42: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 0x7F) + { + goto basic_json_parser_32; + } + if (yych <= 0x8F) + { + goto basic_json_parser_38; + } + goto basic_json_parser_32; +basic_json_parser_43: + yyaccept = 2; + yych = *(m_marker = ++m_cursor); + if (yych <= '9') + { + if (yych == '.') + { + goto basic_json_parser_47; + } + if (yych >= '0') + { + goto basic_json_parser_48; + } + } + else + { + if (yych <= 'E') + { + if (yych >= 'E') + { + goto basic_json_parser_51; + } + } + else + { + if (yych == 'e') + { + goto basic_json_parser_51; + } + } + } +basic_json_parser_44: + { + last_token_type = token_type::value_integer; + break; + } +basic_json_parser_45: + yyaccept = 2; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(3); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '9') + { + if (yych == '.') + { + goto basic_json_parser_47; + } + if (yych <= '/') + { + goto basic_json_parser_44; + } + goto basic_json_parser_45; + } + else + { + if (yych <= 'E') + { + if (yych <= 'D') + { + goto basic_json_parser_44; + } + goto basic_json_parser_51; + } + else + { + if (yych == 'e') + { + goto basic_json_parser_51; + } + goto basic_json_parser_44; + } + } +basic_json_parser_47: + yych = *++m_cursor; if (yych <= '/') { - goto basic_json_parser_33; + goto basic_json_parser_32; } if (yych <= '9') { - goto basic_json_parser_46; + goto basic_json_parser_56; } - goto basic_json_parser_33; - } -basic_json_parser_45: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } -basic_json_parser_46: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '/') - { - goto basic_json_parser_25; - } - if (yych <= '9') - { - goto basic_json_parser_46; - } - goto basic_json_parser_25; + goto basic_json_parser_32; basic_json_parser_48: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= 'D') - { + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; if (yych <= '/') { - goto basic_json_parser_25; + goto basic_json_parser_50; } if (yych <= '9') { goto basic_json_parser_48; } - goto basic_json_parser_25; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_25; - } basic_json_parser_50: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') - { - if (yych == '.') { - goto basic_json_parser_43; + last_token_type = token_type::parse_error; + break; } - goto basic_json_parser_25; - } - else - { - if (yych <= 'E') +basic_json_parser_51: + yych = *++m_cursor; + if (yych <= ',') { - goto basic_json_parser_44; + if (yych == '+') + { + goto basic_json_parser_59; + } + goto basic_json_parser_32; } + else + { + if (yych <= '-') + { + goto basic_json_parser_59; + } + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_60; + } + goto basic_json_parser_32; + } +basic_json_parser_52: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_62; + } + goto basic_json_parser_32; +basic_json_parser_53: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_63; + } + goto basic_json_parser_32; +basic_json_parser_54: + yych = *++m_cursor; + if (yych == 'u') + { + goto basic_json_parser_64; + } + goto basic_json_parser_32; +basic_json_parser_55: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_65; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_65; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_65; + } + goto basic_json_parser_32; + } +basic_json_parser_56: + yyaccept = 3; + m_marker = ++m_cursor; + if ((m_limit - m_cursor) < 3) + { + fill_line_buffer(3); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= 'D') + { + if (yych <= '/') + { + goto basic_json_parser_58; + } + if (yych <= '9') + { + goto basic_json_parser_56; + } + } + else + { + if (yych <= 'E') + { + goto basic_json_parser_51; + } + if (yych == 'e') + { + goto basic_json_parser_51; + } + } +basic_json_parser_58: + { + last_token_type = token_type::value_float; + break; + } +basic_json_parser_59: + yych = *++m_cursor; + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych >= ':') + { + goto basic_json_parser_32; + } +basic_json_parser_60: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '/') + { + goto basic_json_parser_58; + } + if (yych <= '9') + { + goto basic_json_parser_60; + } + goto basic_json_parser_58; +basic_json_parser_62: + yych = *++m_cursor; + if (yych == 's') + { + goto basic_json_parser_66; + } + goto basic_json_parser_32; +basic_json_parser_63: + yych = *++m_cursor; + if (yych == 'l') + { + goto basic_json_parser_67; + } + goto basic_json_parser_32; +basic_json_parser_64: + yych = *++m_cursor; if (yych == 'e') { - goto basic_json_parser_44; + goto basic_json_parser_69; + } + goto basic_json_parser_32; +basic_json_parser_65: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_71; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_71; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_71; + } + goto basic_json_parser_32; + } +basic_json_parser_66: + yych = *++m_cursor; + if (yych == 'e') + { + goto basic_json_parser_72; + } + goto basic_json_parser_32; +basic_json_parser_67: + ++m_cursor; + { + last_token_type = token_type::literal_null; + break; + } +basic_json_parser_69: + ++m_cursor; + { + last_token_type = token_type::literal_true; + break; + } +basic_json_parser_71: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_74; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_74; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_74; + } + goto basic_json_parser_32; + } +basic_json_parser_72: + ++m_cursor; + { + last_token_type = token_type::literal_false; + break; + } +basic_json_parser_74: + ++m_cursor; + if (m_limit <= m_cursor) + { + fill_line_buffer(1); // LCOV_EXCL_LINE + } + yych = *m_cursor; + if (yych <= '@') + { + if (yych <= '/') + { + goto basic_json_parser_32; + } + if (yych <= '9') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; + } + else + { + if (yych <= 'F') + { + goto basic_json_parser_30; + } + if (yych <= '`') + { + goto basic_json_parser_32; + } + if (yych <= 'f') + { + goto basic_json_parser_30; + } + goto basic_json_parser_32; } - goto basic_json_parser_25; - } -basic_json_parser_51: - yych = *++m_cursor; - if (yych != 'l') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 's') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 'e') - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return token_type::literal_false; - } -basic_json_parser_56: - yych = *++m_cursor; - if (yych != 'u') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 'e') - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return token_type::literal_true; - } -basic_json_parser_60: - yych = *++m_cursor; - if (yych != 'l') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 'l') - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return token_type::literal_null; - } -basic_json_parser_64: - yych = *++m_cursor; - if (yych != 0xBF) - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return scan(); } + } - + return last_token_type; } - /// append data from the stream to the internal buffer - void yyfill() noexcept + /*! + @brief append data from the stream to the line buffer + + This function is called by the scan() function when the end of the + buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be + incremented without leaving the limits of the line buffer. Note re2c + decides when to call this function. + + If the lexer reads from contiguous storage, there is no trailing null + byte. Therefore, this function must make sure to add these padding + null bytes. + + If the lexer reads from an input stream, this function reads the next + line of the input. + + @pre + p p p p p p u u u u u x . . . . . . + ^ ^ ^ ^ + m_content m_start | m_limit + m_cursor + + @post + u u u u u x x x x x x x . . . . . . + ^ ^ ^ + | m_cursor m_limit + m_start + m_content + */ + void fill_line_buffer(size_t n = 0) { - if (m_stream == nullptr or not * m_stream) + // if line buffer is used, m_content points to its data + assert(m_line_buffer.empty() + or m_content == reinterpret_cast(m_line_buffer.data())); + + // if line buffer is used, m_limit is set past the end of its data + assert(m_line_buffer.empty() + or m_limit == m_content + m_line_buffer.size()); + + // pointer relationships + assert(m_content <= m_start); + assert(m_start <= m_cursor); + assert(m_cursor <= m_limit); + assert(m_marker == nullptr or m_marker <= m_limit); + + // number of processed characters (p) + const auto num_processed_chars = static_cast(m_start - m_content); + // offset for m_marker wrt. to m_start + const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; + // number of unprocessed characters (u) + const auto offset_cursor = m_cursor - m_start; + + // no stream is used or end of file is reached + if (m_stream == nullptr or m_stream->eof()) { - return; + // m_start may or may not be pointing into m_line_buffer at + // this point. We trust the standard library to do the right + // thing. See http://stackoverflow.com/q/28142011/266378 + m_line_buffer.assign(m_start, m_limit); + + // append n characters to make sure that there is sufficient + // space between m_cursor and m_limit + m_line_buffer.append(1, '\x00'); + if (n > 0) + { + m_line_buffer.append(n - 1, '\x01'); + } + } + else + { + // delete processed characters from line buffer + m_line_buffer.erase(0, num_processed_chars); + // read next line from input stream + m_line_buffer_tmp.clear(); + std::getline(*m_stream, m_line_buffer_tmp, '\n'); + + // add line with newline symbol to the line buffer + m_line_buffer += m_line_buffer_tmp; + m_line_buffer.push_back('\n'); } - const ssize_t offset_start = m_start - m_content; - const ssize_t offset_marker = m_marker - m_start; - const ssize_t offset_cursor = m_cursor - m_start; - - m_buffer.erase(0, static_cast(offset_start)); - std::string line; - assert(m_stream != nullptr); - std::getline(*m_stream, line); - m_buffer += "\n" + line; // add line with newline symbol - - m_content = reinterpret_cast(m_buffer.c_str()); + // set pointers + m_content = reinterpret_cast(m_line_buffer.data()); assert(m_content != nullptr); m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + m_buffer.size() - 1; + m_limit = m_start + m_line_buffer.size(); } /// return string representation of last read token - string_t get_token() const noexcept + string_t get_token_string() const { assert(m_start != nullptr); return string_t(reinterpret_cast(m_start), @@ -7664,26 +10927,75 @@ basic_json_parser_64: 1. Escaped characters. In this case, a new character is constructed according to the nature of the escape. Some escapes create new - characters (e.g., @c "\\n" is replaced by @c "\n"), some are copied - as is (e.g., @c "\\\\"). Furthermore, Unicode escapes of the shape - @c "\\uxxxx" need special care. In this case, to_unicode takes care + characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied + as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape + `"\\uxxxx"` need special care. In this case, to_unicode takes care of the construction of the values. 2. Unescaped characters are copied as is. - @return string value of current token without opening and closing quotes + @pre `m_cursor - m_start >= 2`, meaning the length of the last token + is at least 2 bytes which is trivially true for any string (which + consists of at least two quotes). + + " c1 c2 c3 ... " + ^ ^ + m_start m_cursor + + @complexity Linear in the length of the string.\n + + Lemma: The loop body will always terminate.\n + + Proof (by contradiction): Assume the loop body does not terminate. As + the loop body does not contain another loop, one of the called + functions must never return. The called functions are `std::strtoul` + and to_unicode. Neither function can loop forever, so the loop body + will never loop forever which contradicts the assumption that the loop + body does not terminate, q.e.d.\n + + Lemma: The loop condition for the for loop is eventually false.\n + + Proof (by contradiction): Assume the loop does not terminate. Due to + the above lemma, this can only be due to a tautological loop + condition; that is, the loop condition i < m_cursor - 1 must always be + true. Let x be the change of i for any loop iteration. Then + m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This + can be rephrased to m_cursor - m_start - 2 > x. With the + precondition, we x <= 0, meaning that the loop condition holds + indefinitely if i is always decreased. However, observe that the value + of i is strictly increasing with each iteration, as it is incremented + by 1 in the iteration expression and never decremented inside the loop + body. Hence, the loop condition will eventually be false which + contradicts the assumption that the loop condition is a tautology, + q.e.d. + + @return string value of current token without opening and closing + quotes @throw std::out_of_range if to_unicode fails */ string_t get_string() const { + assert(m_cursor - m_start >= 2); + string_t result; result.reserve(static_cast(m_cursor - m_start - 2)); // iterate the result between the quotes for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) { - // process escaped characters - if (*i == '\\') + // find next escape character + auto e = std::find(i, m_cursor - 1, '\\'); + if (e != i) { + // see https://github.com/nlohmann/json/issues/365#issuecomment-262874705 + for (auto k = i; k < e; k++) + { + result.push_back(static_cast(*k)); + } + i = e - 1; // -1 because of ++i + } + else + { + // processing escaped character // read next character ++i; @@ -7744,7 +11056,7 @@ basic_json_parser_64: // make sure there is a subsequent unicode if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') { - throw std::invalid_argument("missing low surrogate"); + JSON_THROW(std::invalid_argument("missing low surrogate")); } // get code yyyy from uxxxx\uyyyy @@ -7754,6 +11066,11 @@ basic_json_parser_64: // skip the next 10 characters (xxxx\uyyyy) i += 10; } + else if (codepoint >= 0xDC00 and codepoint <= 0xDFFF) + { + // we found a lone low surrogate + JSON_THROW(std::invalid_argument("missing high surrogate")); + } else { // add unicode character(s) @@ -7765,88 +11082,255 @@ basic_json_parser_64: } } } - else - { - // all other characters are just copied to the end of the - // string - result.append(1, static_cast(*i)); - } } return result; } + /*! - @brief parse floating point number + @brief parse string into a built-in arithmetic type as if the current + locale is POSIX. - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function (i.e., `std::strtof`, - `std::strtod`, or `std::strtold`) based on the type supplied via the - first parameter. Set this to @a static_cast(nullptr). + @note in floating-point case strtod may parse past the token's end - + this is not an error - @param[in] type the @ref number_float_t in use - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - - @bug This function uses `std::strtof`, `std::strtod`, or `std::strtold` - which use the current C locale to determine which character is used as - decimal point character. This may yield to parse errors if the locale - does not used `.`. + @note any leading blanks are not handled */ - long double str_to_float_t(long double* /* type */, char** endptr) const + struct strtonum { - return std::strtold(reinterpret_cast(m_start), endptr); - } + public: + strtonum(const char* start, const char* end) + : m_start(start), m_end(end) + {} - /// @copydoc str_to_float_t - double str_to_float_t(double*, char** endptr) const - { - return std::strtod(reinterpret_cast(m_start), endptr); - } + /*! + @return true iff parsed successfully as number of type T - /// @copydoc str_to_float_t - float str_to_float_t(float*, char** endptr) const - { - return std::strtof(reinterpret_cast(m_start), endptr); - } + @param[in,out] val shall contain parsed value, or undefined value + if could not parse + */ + template::value>::type> + bool to(T& val) const + { + return parse(val, std::is_integral()); + } + + private: + const char* const m_start = nullptr; + const char* const m_end = nullptr; + + // floating-point conversion + + // overloaded wrappers for strtod/strtof/strtold + // that will be called from parse + static void strtof(float& f, const char* str, char** endptr) + { + f = std::strtof(str, endptr); + } + + static void strtof(double& f, const char* str, char** endptr) + { + f = std::strtod(str, endptr); + } + + static void strtof(long double& f, const char* str, char** endptr) + { + f = std::strtold(str, endptr); + } + + template + bool parse(T& value, /*is_integral=*/std::false_type) const + { + // replace decimal separator with locale-specific version, + // when necessary; data will point to either the original + // string, or buf, or tempstr containing the fixed string. + std::string tempstr; + std::array buf; + const size_t len = static_cast(m_end - m_start); + + // lexer will reject empty numbers + assert(len > 0); + + // since dealing with strtod family of functions, we're + // getting the decimal point char from the C locale facilities + // instead of C++'s numpunct facet of the current std::locale + const auto loc = localeconv(); + assert(loc != nullptr); + const char decimal_point_char = (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0]; + + const char* data = m_start; + + if (decimal_point_char != '.') + { + const size_t ds_pos = static_cast(std::find(m_start, m_end, '.') - m_start); + + if (ds_pos != len) + { + // copy the data into the local buffer or tempstr, if + // buffer is too small; replace decimal separator, and + // update data to point to the modified bytes + if ((len + 1) < buf.size()) + { + std::copy(m_start, m_end, buf.begin()); + buf[len] = 0; + buf[ds_pos] = decimal_point_char; + data = buf.data(); + } + else + { + tempstr.assign(m_start, m_end); + tempstr[ds_pos] = decimal_point_char; + data = tempstr.c_str(); + } + } + } + + char* endptr = nullptr; + value = 0; + // this calls appropriate overload depending on T + strtof(value, data, &endptr); + + // parsing was successful iff strtof parsed exactly the number + // of characters determined by the lexer (len) + const bool ok = (endptr == (data + len)); + + if (ok and (value == static_cast(0.0)) and (*data == '-')) + { + // some implementations forget to negate the zero + value = -0.0; + } + + return ok; + } + + // integral conversion + + signed long long parse_integral(char** endptr, /*is_signed*/std::true_type) const + { + return std::strtoll(m_start, endptr, 10); + } + + unsigned long long parse_integral(char** endptr, /*is_signed*/std::false_type) const + { + return std::strtoull(m_start, endptr, 10); + } + + template + bool parse(T& value, /*is_integral=*/std::true_type) const + { + char* endptr = nullptr; + errno = 0; // these are thread-local + const auto x = parse_integral(&endptr, std::is_signed()); + + // called right overload? + static_assert(std::is_signed() == std::is_signed(), ""); + + value = static_cast(x); + + return (x == static_cast(value)) // x fits into destination T + and (x < 0) == (value < 0) // preserved sign + //and ((x != 0) or is_integral()) // strto[u]ll did nto fail + and (errno == 0) // strto[u]ll did not overflow + and (m_start < m_end) // token was not empty + and (endptr == m_end); // parsed entire token exactly + } + }; /*! @brief return number value for number tokens - This function translates the last token into a floating point number. - The pointer m_start points to the beginning of the parsed number. We - pass this pointer to std::strtod which sets endptr to the first - character past the converted number. If this pointer is not the same as - m_cursor, then either more or less characters have been used during the - comparison. This can happen for inputs like "01" which will be treated - like number 0 followed by number 1. + This function translates the last token into the most appropriate + number type (either integer, unsigned integer or floating point), + which is passed back to the caller via the result parameter. - @return the result of the number conversion or NAN if the conversion - read past the current token. The latter case needs to be treated by the - caller function. + integral numbers that don't fit into the the range of the respective + type are parsed as number_float_t - @throw std::range_error if passed value is out of range + floating-point values do not satisfy std::isfinite predicate + are converted to value_t::null + + throws if the entire string [m_start .. m_cursor) cannot be + interpreted as a number + + @param[out] result @ref basic_json object to receive the number. + @param[in] token the type of the number token */ - number_float_t get_number() const + bool get_number(basic_json& result, const token_type token) const { - // conversion - typename string_t::value_type* endptr; assert(m_start != nullptr); - number_float_t float_val = str_to_float_t(static_cast(nullptr), &endptr); + assert(m_start < m_cursor); + assert((token == token_type::value_unsigned) or + (token == token_type::value_integer) or + (token == token_type::value_float)); - // return float_val if the whole number was translated and NAN - // otherwise - return (reinterpret_cast(endptr) == m_cursor) ? float_val : NAN; + strtonum num_converter(reinterpret_cast(m_start), + reinterpret_cast(m_cursor)); + + switch (token) + { + case lexer::token_type::value_unsigned: + { + number_unsigned_t val; + if (num_converter.to(val)) + { + // parsing successful + result.m_type = value_t::number_unsigned; + result.m_value = val; + return true; + } + break; + } + + case lexer::token_type::value_integer: + { + number_integer_t val; + if (num_converter.to(val)) + { + // parsing successful + result.m_type = value_t::number_integer; + result.m_value = val; + return true; + } + break; + } + + default: + { + break; + } + } + + // parse float (either explicitly or because a previous conversion + // failed) + number_float_t val; + if (num_converter.to(val)) + { + // parsing successful + result.m_type = value_t::number_float; + result.m_value = val; + + // replace infinity and NAN by null + if (not std::isfinite(result.m_value.number_float)) + { + result.m_type = value_t::null; + result.m_value = basic_json::json_value(); + } + + return true; + } + + // couldn't parse number in any format + return false; } private: /// optional input stream std::istream* m_stream = nullptr; - /// the buffer - string_t m_buffer; + /// line buffer buffer for m_stream + string_t m_line_buffer {}; + /// used for filling m_line_buffer + string_t m_line_buffer_tmp {}; /// the buffer pointer const lexer_char_t* m_content = nullptr; /// pointer to the beginning of the current symbol @@ -7857,6 +11341,8 @@ basic_json_parser_64: const lexer_char_t* m_cursor = nullptr; /// pointer to the end of the buffer const lexer_char_t* m_limit = nullptr; + /// the last token type + token_type last_token_type = token_type::end_of_input; }; /*! @@ -7867,32 +11353,42 @@ basic_json_parser_64: class parser { public: - /// constructor for strings - parser(const string_t& s, parser_callback_t cb = nullptr) - : callback(cb), m_lexer(s) - { - // read first token - get_token(); - } + /// a parser reading from a string literal + parser(const char* buff, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast(buff), std::strlen(buff)) + {} /// a parser reading from an input stream - parser(std::istream& _is, parser_callback_t cb = nullptr) - : callback(cb), m_lexer(&_is) - { - // read first token - get_token(); - } + parser(std::istream& is, const parser_callback_t cb = nullptr) + : callback(cb), m_lexer(is) + {} + + /// a parser reading from an iterator range with contiguous storage + template::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) + : callback(cb), + m_lexer(reinterpret_cast(&(*first)), + static_cast(std::distance(first, last))) + {} /// public parser interface basic_json parse() { + // read first token + get_token(); + basic_json result = parse_internal(true); + result.assert_invariant(); expect(lexer::token_type::end_of_input); // return parser result and replace it with null in case the // top-level value was discarded by the callback function - return result.is_discarded() ? basic_json() : result; + return result.is_discarded() ? basic_json() : std::move(result); } private: @@ -7905,11 +11401,12 @@ basic_json_parser_64: { case lexer::token_type::begin_object: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { // explicitly set result to object to cope with {} result.m_type = value_t::object; - result.m_value = json_value(value_t::object); + result.m_value = value_t::object; } // read next token @@ -7983,11 +11480,12 @@ basic_json_parser_64: case lexer::token_type::begin_array: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) { // explicitly set result to object to cope with [] result.m_type = value_t::array; - result.m_value = json_value(value_t::array); + result.m_value = value_t::array; } // read next token @@ -8067,34 +11565,12 @@ basic_json_parser_64: break; } - case lexer::token_type::value_number: + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: { - result.m_value = m_lexer.get_number(); - - // NAN is returned if token could not be translated - // completely - if (std::isnan(result.m_value.number_float)) - { - throw std::invalid_argument(std::string("parse error - ") + - m_lexer.get_token() + " is not a number"); - } - + m_lexer.get_number(result, last_token); get_token(); - - // check if conversion loses precision (special case -0.0 always loses precision) - const auto int_val = static_cast(result.m_value.number_float); - if (result.m_value.number_float == static_cast(int_val) and - result.m_value.number_integer != json_value(-0.0f).number_integer) - { - // we would not lose precision -> return int - result.m_type = value_t::number_integer; - result.m_value = int_val; - } - else - { - // we would lose precision -> return float - result.m_type = value_t::number_float; - } break; } @@ -8124,10 +11600,11 @@ basic_json_parser_64: if (t != last_token) { std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : lexer::token_type_name(last_token)); error_msg += "; expected " + lexer::token_type_name(t); - throw std::invalid_argument(error_msg); + JSON_THROW(std::invalid_argument(error_msg)); } } @@ -8136,9 +11613,10 @@ basic_json_parser_64: if (t == last_token) { std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : + error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() + + "'") : lexer::token_type_name(last_token)); - throw std::invalid_argument(error_msg); + JSON_THROW(std::invalid_argument(error_msg)); } } @@ -8146,14 +11624,1278 @@ basic_json_parser_64: /// current level of recursion int depth = 0; /// callback function - parser_callback_t callback; + const parser_callback_t callback = nullptr; /// the type of the last read token typename lexer::token_type last_token = lexer::token_type::uninitialized; /// the lexer lexer m_lexer; }; -}; + public: + /*! + @brief JSON Pointer + + A JSON pointer defines a string syntax for identifying a specific value + within a JSON document. It can be used with functions `at` and + `operator[]`. Furthermore, JSON pointers are the base for JSON patches. + + @sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + class json_pointer + { + /// allow basic_json to access private members + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the + empty string is assumed which references the whole JSON + value + + @throw std::domain_error if reference token is nonempty and does not + begin with a slash (`/`); example: `"JSON pointer must be empty or + begin with /"` + @throw std::domain_error if a tilde (`~`) is not followed by `0` + (representing `~`) or `1` (representing `/`); example: `"escape error: + ~ must be followed with 0 or 1"` + + @liveexample{The example shows the construction several valid JSON + pointers as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`., + json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const noexcept + { + return std::accumulate(reference_tokens.begin(), + reference_tokens.end(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + private: + /// remove and return last reference pointer + std::string pop_back() + { + if (is_root()) + { + JSON_THROW(std::domain_error("JSON pointer has no parent")); + } + + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } + + /// return whether pointer points to the root document + bool is_root() const + { + return reference_tokens.empty(); + } + + json_pointer top() const + { + if (is_root()) + { + JSON_THROW(std::domain_error("JSON pointer has no parent")); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + */ + reference get_and_create(reference j) const + { + pointer result = &j; + + // in case no reference tokens exist, return a reference to the + // JSON value j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->m_type) + { + case value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case value_t::array: + { + // create an entry in the array + result = &result->operator[](static_cast(std::stoi(reference_token))); + break; + } + + /* + The following code is only reached if there exists a + reference token _and_ the current value is primitive. In + this case, we have an error situation, because primitive + values may only occur as single value; that is, with an + empty list of reference tokens. + */ + default: + { + JSON_THROW(std::domain_error("invalid value to unflatten")); + } + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries + to create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + */ + reference get_unchecked(pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->m_type == value_t::null) + { + // check if reference token is a number + const bool nums = std::all_of(reference_token.begin(), + reference_token.end(), + [](const char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object + // otherwise + if (nums or reference_token == "-") + { + *ptr = value_t::array; + } + else + { + *ptr = value_t::object; + } + } + + switch (ptr->m_type) + { + case value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + } + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + reference get_checked(pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + JSON_THROW(std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + */ + const_reference get_unchecked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" cannot be used for const access + JSON_THROW(std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // use unchecked array access + ptr = &ptr->operator[](static_cast(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + const_reference get_checked(const_pointer ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case value_t::array: + { + if (reference_token == "-") + { + // "-" always fails the range check + JSON_THROW(std::out_of_range("array index '-' (" + + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (reference_token.size() > 1 and reference_token[0] == '0') + { + JSON_THROW(std::domain_error("array index must not begin with '0'")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast(std::stoi(reference_token))); + break; + } + + default: + { + JSON_THROW(std::out_of_range("unresolved reference token '" + reference_token + "'")); + } + } + } + + return *ptr; + } + + /// split the string input to reference tokens + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (reference_string[0] != '/') + { + JSON_THROW(std::domain_error("JSON pointer must be empty or begin with '/'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == string::npos+1 = 0 + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1')) + { + JSON_THROW(std::domain_error("escape error: '~' must be followed with '0' or '1'")); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @pre The search string @a f must not be empty. + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, + const std::string& f, + const std::string& t) + { + assert(not f.empty()); + + for ( + size_t pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t + pos = s.find(f, pos + t.size()) // find next occurrence of f + ); + } + + /// escape tilde and slash + static std::string escape(std::string s) + { + // escape "~"" to "~0" and "/" to "~1" + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape tilde and slash + static void unescape(std::string& s) + { + // first transform any occurrence of the sequence '~1' to '/' + replace_substring(s, "~1", "/"); + // then transform any occurrence of the sequence '~0' to '~' + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const basic_json& value, + basic_json& result) + { + switch (value.m_type) + { + case value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), + element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + */ + static basic_json unflatten(const basic_json& value) + { + if (not value.is_object()) + { + JSON_THROW(std::domain_error("only objects can be unflattened")); + } + + basic_json result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (not element.second.is_primitive()) + { + JSON_THROW(std::domain_error("values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note + // that if the JSON pointer is "" (i.e., points to the whole + // value), function get_and_create returns a reference to + // result itself. An assignment will then create a primitive + // value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + private: + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens {}; + }; + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. Similar to @ref operator[](const typename + object_t::key_type&), `null` values are created in arrays and objects if + necessary. + + In particular: + - If the JSON pointer points to an object key that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. + - If the JSON pointer points to an array index that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. All indices between the current maximum and the given + index are also filled with `null`. + - The special value `-` is treated as a synonym for the index past the + end. + + @param[in] ptr a JSON pointer + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,operatorjson_pointer} + + @since version 2.0.0 + */ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. The function does not change the JSON + value; no `null` values are created. In particular, the the special value + `-` yields an exception. + + @param[in] ptr JSON pointer to the desired element + + @return const reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} + + @since version 2.0.0 + */ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a reference to the element at with specified JSON pointer @a ptr, + with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,at_json_pointer} + + @since version 2.0.0 + */ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a const reference to the element at with specified JSON pointer @a + ptr, with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw std::out_of_range if the JSON pointer can not be resolved + @throw std::domain_error if an array index begins with '0' + @throw std::invalid_argument if an array index was not a number + + @liveexample{The behavior is shown in the example.,at_json_pointer_const} + + @since version 2.0.0 + */ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /*! + @brief return flattened JSON value + + The function creates a JSON object whose keys are JSON pointers (see [RFC + 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all + primitive. The original JSON value can be restored using the @ref + unflatten() function. + + @return an object that maps JSON pointers to primitive values + + @note Empty objects and arrays are flattened to `null` and will not be + reconstructed correctly by the @ref unflatten() function. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a JSON object is flattened to an + object whose keys consist of JSON pointers.,flatten} + + @sa @ref unflatten() for the reverse function + + @since version 2.0.0 + */ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /*! + @brief unflatten a previously flattened JSON value + + The function restores the arbitrary nesting of a JSON value that has been + flattened before using the @ref flatten() function. The JSON value must + meet certain constraints: + 1. The value must be an object. + 2. The keys must be JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) + 3. The mapped values must be primitive JSON types. + + @return the original JSON from a flattened version + + @note Empty objects and arrays are flattened by @ref flatten() to `null` + values and can not unflattened to their original type. Apart from + this example, for a JSON value `j`, the following is always true: + `j == j.flatten().unflatten()`. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a flattened JSON object is + unflattened into the original nested JSON object.,unflatten} + + @sa @ref flatten() for the reverse function + + @since version 2.0.0 + */ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /*! + @brief applies a JSON patch + + [JSON Patch](http://jsonpatch.com) defines a JSON document structure for + expressing a sequence of operations to apply to a JSON) document. With + this function, a JSON Patch is applied to the current JSON value by + executing all operations from the patch. + + @param[in] json_patch JSON patch document + @return patched document + + @note The application of a patch is atomic: Either all operations succeed + and the patched document is returned or an exception is thrown. In + any case, the original value is not changed: the patch is applied + to a copy of the value. + + @throw std::out_of_range if a JSON pointer inside the patch could not + be resolved successfully in the current JSON value; example: `"key baz + not found"` + @throw invalid_argument if the JSON patch is malformed (e.g., mandatory + attributes are missing); example: `"operation add must have member path"` + + @complexity Linear in the size of the JSON value and the length of the + JSON patch. As usually only a fraction of the JSON value is affected by + the patch, the complexity can usually be neglected. + + @liveexample{The following code shows how a JSON patch is applied to a + value.,patch} + + @sa @ref diff -- create a JSON patch by comparing two JSON values + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.is_root()) + { + result = val; + } + else + { + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = std::stoi(last_path); + if (static_cast(idx) > parent.size()) + { + // avoid undefined behavior + JSON_THROW(std::out_of_range("array index " + std::to_string(idx) + " is out of range")); + } + else + { + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + } + break; + } + + default: + { + // if there exists a parent it cannot be primitive + assert(false); // LCOV_EXCL_LINE + } + } + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [&result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (it != parent.end()) + { + parent.erase(it); + } + else + { + JSON_THROW(std::out_of_range("key '" + last_path + "' not found")); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(static_cast(std::stoi(last_path))); + } + }; + + // type check + if (not json_patch.is_array()) + { + // a JSON patch must be an array of objects + JSON_THROW(std::invalid_argument("JSON patch must be an array of objects")); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json& + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (it == val.m_value.object->end()) + { + JSON_THROW(std::invalid_argument(error_msg + " must have member '" + member + "'")); + } + + // check if result is of type string + if (string_type and not it->second.is_string()) + { + JSON_THROW(std::invalid_argument(error_msg + " must have string member '" + member + "'")); + } + + // no error: return value + return it->second; + }; + + // type check + if (not val.is_object()) + { + JSON_THROW(std::invalid_argument("JSON patch must be an array of objects")); + } + + // collect mandatory members + const std::string op = get_value("op", "op", true); + const std::string path = get_value(op, "path", true); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const std::string from_path = get_value("move", "from", true); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const std::string from_path = get_value("copy", "from", true);; + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + result[ptr] = result.at(from_ptr); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_CATCH (std::out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (not success) + { + JSON_THROW(std::domain_error("unsuccessful: " + val.dump())); + } + + break; + } + + case patch_operations::invalid: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(std::invalid_argument("operation value '" + op + "' is invalid")); + } + } + } + + return result; + } + + /*! + @brief creates a diff as a JSON patch + + Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can + be changed into the value @a target by calling @ref patch function. + + @invariant For two JSON values @a source and @a target, the following code + yields always `true`: + @code {.cpp} + source.patch(diff(source, target)) == target; + @endcode + + @note Currently, only `remove`, `add`, and `replace` operations are + generated. + + @param[in] source JSON value to compare from + @param[in] target JSON value to compare against + @param[in] path helper value to create JSON pointers + + @return a JSON patch to convert the @a source to @a target + + @complexity Linear in the lengths of @a source and @a target. + + @liveexample{The following code shows how a JSON patch is created as a + diff for two JSON values.,diff} + + @sa @ref patch -- apply a JSON patch + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + + @since version 2.0.0 + */ + static basic_json diff(const basic_json& source, + const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + } + else + { + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + size_t i = 0; + while (i < source.size() and i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // i now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/" + std::to_string(i)}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.begin(); it != source.end(); ++it) + { + // escape the key name to be used in a JSON patch + const auto key = json_pointer::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, + {"path", path + "/" + key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.begin(); it != target.end(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto key = json_pointer::escape(it.key()); + result.push_back( + { + {"op", "add"}, + {"path", path + "/" + key}, + {"value", it.value()} + }); + } + } + + break; + } + + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, + {"path", path}, + {"value", target} + }); + break; + } + } + } + + return result; + } + + /// @} +}; ///////////// // presets // @@ -8162,18 +12904,18 @@ basic_json_parser_64: /*! @brief default JSON class -This type is the default specialization of the @ref basic_json class which uses -the standard template types. +This type is the default specialization of the @ref basic_json class which +uses the standard template types. @since version 1.0.0 */ using json = basic_json<>; -} +} // namespace nlohmann -///////////////////////// -// nonmember functions // -///////////////////////// +/////////////////////// +// nonmember support // +/////////////////////// // specialization of std::swap, and std::hash namespace std @@ -8183,7 +12925,7 @@ namespace std @since version 1.0.0 */ -template <> +template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and @@ -8194,7 +12936,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template <> +template<> struct hash { /*! @@ -8209,23 +12951,42 @@ struct hash return h(j.dump()); } }; -} +} // namespace std /*! @brief user-defined string literal for JSON values -This operator implements a user-defined string literal for JSON objects. It can -be used by adding \p "_json" to a string literal and returns a JSON object if -no parse error occurred. +This operator implements a user-defined string literal for JSON objects. It +can be used by adding `"_json"` to a string literal and returns a JSON object +if no parse error occurred. @param[in] s a string representation of a JSON object +@param[in] n the length of string @a s @return a JSON object @since version 1.0.0 */ -inline nlohmann::json operator "" _json(const char* s, std::size_t) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) { - return nlohmann::json::parse(reinterpret_cast(s)); + return nlohmann::json::parse(s, s + n); +} + +/*! +@brief user-defined string literal for JSON pointer + +This operator implements a user-defined string literal for JSON Pointers. It +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer +object if no parse error occurred. + +@param[in] s a string representation of a JSON Pointer +@param[in] n the length of string @a s +@return a JSON pointer object + +@since version 2.0.0 +*/ +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); } // restore GCC/clang diagnostic settings @@ -8233,4 +12994,10 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t) #pragma GCC diagnostic pop #endif +// clean up +#undef JSON_CATCH +#undef JSON_DEPRECATED +#undef JSON_THROW +#undef JSON_TRY + #endif diff --git a/ext/sgct b/ext/sgct index 40ea897067..b94aa799b5 160000 --- a/ext/sgct +++ b/ext/sgct @@ -1 +1 @@ -Subproject commit 40ea8970673c58a172dabab6ddccc45f0a0e92ac +Subproject commit b94aa799b5b7a5bfff079fc105fcc0be952b3901 diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index 366ff8ea58..db7cff07ea 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -85,9 +85,19 @@ public: DownloadManager(std::string requestURL, int applicationVersion, bool useMultithreadedDownload = true); - // callbacks happen on a different thread - std::shared_ptr downloadFile(const std::string& url, const ghoul::filesystem::File& file, + //downloadFile + // url - specifies the target of the download + // file - specifies path to local saved file + // overrideFile - if true, overrides existing file of same name + // failOnError - if true, http codes >= 400 (client/server errors) result in fail + // timeout_secs - timeout in seconds before giving up on download (0 = no timeout) + // finishedCallback - callback when download finished (happens on different thread) + // progressCallback - callback for status during (happens on different thread) + std::shared_ptr downloadFile(const std::string& url, + const ghoul::filesystem::File& file, bool overrideFile = true, + bool failOnError = false, + unsigned int timeout_secs = 0, DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), DownloadProgressCallback progressCallback = DownloadProgressCallback() ); diff --git a/include/openspace/network/messagestructures.h b/include/openspace/network/messagestructures.h index ee2ffc71a9..b3943d7fed 100644 --- a/include/openspace/network/messagestructures.h +++ b/include/openspace/network/messagestructures.h @@ -66,7 +66,7 @@ struct CameraKeyframe { // Follow focus node rotation? buffer.insert(buffer.end(), reinterpret_cast(&_followNodeRotation), reinterpret_cast(&_followNodeRotation) + sizeof(_followNodeRotation)); - int nodeNameLength = _focusNode.size(); + int nodeNameLength = static_cast(_focusNode.size()); // Add focus node buffer.insert(buffer.end(), reinterpret_cast(&nodeNameLength), reinterpret_cast(&nodeNameLength) + sizeof(nodeNameLength)); diff --git a/include/openspace/util/timeline.h b/include/openspace/util/timeline.h index 794aecf96a..9c96f0b582 100644 --- a/include/openspace/util/timeline.h +++ b/include/openspace/util/timeline.h @@ -91,6 +91,6 @@ bool compareKeyframeTimeWithTime(const KeyframeBase& a, double b); } // namespace openspace -#include ; +#include #endif // __OPENSPACE_CORE___TIMELINE___H__ diff --git a/modules/fieldlines/rendering/renderablefieldlines.cpp b/modules/fieldlines/rendering/renderablefieldlines.cpp index 9a61c48b46..a6dc9e6f52 100644 --- a/modules/fieldlines/rendering/renderablefieldlines.cpp +++ b/modules/fieldlines/rendering/renderablefieldlines.cpp @@ -325,7 +325,7 @@ void RenderableFieldlines::loadSeedPoints() { loadSeedPointsFromTable(); break; default: - ghoul_assert(false, "Missing case label"); + throw ghoul::MissingCaseException(); } } diff --git a/modules/globebrowsing/geometry/geodeticpatch.cpp b/modules/globebrowsing/geometry/geodeticpatch.cpp index f1e04122ab..72f515d91b 100644 --- a/modules/globebrowsing/geometry/geodeticpatch.cpp +++ b/modules/globebrowsing/geometry/geodeticpatch.cpp @@ -92,7 +92,7 @@ Geodetic2 GeodeticPatch::getCorner(Quad q) const { case NORTH_EAST: return Geodetic2(maxLat(), maxLon());// northEastCorner(); case SOUTH_WEST: return Geodetic2(minLat(), minLon());// southWestCorner(); case SOUTH_EAST: return Geodetic2(minLat(), maxLon());// southEastCorner(); - default: ghoul_assert(false, "Missing case label"); + default: throw ghoul::MissingCaseException(); } } diff --git a/modules/globebrowsing/other/concurrentjobmanager.h b/modules/globebrowsing/other/concurrentjobmanager.h index c90b9576d0..910a02ef1f 100644 --- a/modules/globebrowsing/other/concurrentjobmanager.h +++ b/modules/globebrowsing/other/concurrentjobmanager.h @@ -28,6 +28,8 @@ #include #include +#include + namespace openspace { namespace globebrowsing { @@ -62,6 +64,7 @@ public: private: ConcurrentQueue>> _finishedJobs; std::shared_ptr threadPool; + std::mutex _finishedJobsMutex; }; } // namespace globebrowsing diff --git a/modules/globebrowsing/other/concurrentjobmanager.inl b/modules/globebrowsing/other/concurrentjobmanager.inl index e1c3b5244c..a522216f5b 100644 --- a/modules/globebrowsing/other/concurrentjobmanager.inl +++ b/modules/globebrowsing/other/concurrentjobmanager.inl @@ -42,6 +42,7 @@ template void ConcurrentJobManager

::enqueueJob(std::shared_ptr> job) { threadPool->enqueue([this, job]() { job->execute(); + std::lock_guard lock(_finishedJobsMutex); _finishedJobs.push(job); }); } @@ -54,6 +55,7 @@ void ConcurrentJobManager

::clearEnqueuedJobs() { template std::shared_ptr> ConcurrentJobManager

::popFinishedJob() { ghoul_assert(_finishedJobs.size() > 0, "There is no finished job to pop!"); + std::lock_guard lock(_finishedJobsMutex); return _finishedJobs.pop(); } diff --git a/modules/globebrowsing/other/prioritizingconcurrentjobmanager.h b/modules/globebrowsing/other/prioritizingconcurrentjobmanager.h index ed2cd70841..f2314b07f5 100644 --- a/modules/globebrowsing/other/prioritizingconcurrentjobmanager.h +++ b/modules/globebrowsing/other/prioritizingconcurrentjobmanager.h @@ -29,6 +29,8 @@ #include #include +#include + namespace openspace { namespace globebrowsing { @@ -85,6 +87,7 @@ private: ConcurrentQueue>> _finishedJobs; /// An LRU thread pool is used since the jobs can be bumped and hence prioritized. std::shared_ptr> _threadPool; + std::mutex _finishedJobsMutex; }; } // namespace globebrowsing diff --git a/modules/globebrowsing/other/prioritizingconcurrentjobmanager.inl b/modules/globebrowsing/other/prioritizingconcurrentjobmanager.inl index 1a4444e0eb..ebe0147947 100644 --- a/modules/globebrowsing/other/prioritizingconcurrentjobmanager.inl +++ b/modules/globebrowsing/other/prioritizingconcurrentjobmanager.inl @@ -39,6 +39,7 @@ void PrioritizingConcurrentJobManager::enqueueJob(std::shared_ptrenqueue([this, job]() { job->execute(); + std::lock_guard lock(_finishedJobsMutex); _finishedJobs.push(job); }, key); } @@ -68,7 +69,9 @@ void PrioritizingConcurrentJobManager::clearEnqueuedJobs() { template std::shared_ptr> PrioritizingConcurrentJobManager::popFinishedJob() { ghoul_assert(_finishedJobs.size() > 0, "There is no finished job to pop!"); - return _finishedJobs.pop(); + std::lock_guard lock(_finishedJobsMutex); + std::shared_ptr> result = _finishedJobs.pop(); + return result; } template diff --git a/modules/globebrowsing/tile/pixelregion.cpp b/modules/globebrowsing/tile/pixelregion.cpp index b24d7b263d..4375afd19a 100644 --- a/modules/globebrowsing/tile/pixelregion.cpp +++ b/modules/globebrowsing/tile/pixelregion.cpp @@ -240,16 +240,11 @@ int PixelRegion::area() const { int PixelRegion::edge(Side side) const { switch (side) { - case Side::LEFT: - return start.x; - case Side::TOP: - return start.y; - case Side::RIGHT: - return start.x + numPixels.x; - case Side::BOTTOM: - return start.y + numPixels.y; - default: - ghoul_assert(false, "Missing case label"); + case Side::LEFT: return start.x; + case Side::TOP: return start.y; + case Side::RIGHT: return start.x + numPixels.x; + case Side::BOTTOM: return start.y + numPixels.y; + default: throw ghoul::MissingCaseException(); } } diff --git a/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.cpp b/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.cpp index 8eb7e070a2..904cbe2b13 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.cpp +++ b/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.cpp @@ -65,10 +65,15 @@ GdalRawTileDataReader::GdalRawTileDataReader(const std::string& filePath, { _initDirectory = baseDirectory.empty() ? CPLGetCurrentDir() : baseDirectory; _datasetFilePath = filePath; - ensureInitialized(); + + { // Aquire lock + std::lock_guard lockGuard(_datasetLock); + initialize(); + } } GdalRawTileDataReader::~GdalRawTileDataReader() { + std::lock_guard lockGuard(_datasetLock); if (_dataset != nullptr) { GDALClose(_dataset); _dataset = nullptr; @@ -76,6 +81,7 @@ GdalRawTileDataReader::~GdalRawTileDataReader() { } void GdalRawTileDataReader::reset() { + std::lock_guard lockGuard(_datasetLock); _cached._maxLevel = -1; if (_dataset != nullptr) { GDALClose(_dataset); @@ -84,52 +90,32 @@ void GdalRawTileDataReader::reset() { initialize(); } -int GdalRawTileDataReader::maxChunkLevel() { - ensureInitialized(); - if (_cached._maxLevel < 0) { - int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount(); - _cached._maxLevel = -_cached._tileLevelDifference; - if (numOverviews > 0) { - _cached._maxLevel += numOverviews - 1; - } - } +int GdalRawTileDataReader::maxChunkLevel() const { return _cached._maxLevel; } float GdalRawTileDataReader::noDataValueAsFloat() const { - float noDataValue; - if (_dataset && _dataset->GetRasterBand(1)) { - noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue(); - } - else { - noDataValue = std::numeric_limits::min(); - } - return noDataValue; + return _gdalDatasetMetaDataCached.noDataValue; } int GdalRawTileDataReader::rasterXSize() const { - return _dataset->GetRasterXSize(); + return _gdalDatasetMetaDataCached.rasterXSize; } int GdalRawTileDataReader::rasterYSize() const { - return _dataset->GetRasterYSize(); + return _gdalDatasetMetaDataCached.rasterYSize; } float GdalRawTileDataReader::depthOffset() const { - return _dataset->GetRasterBand(1)->GetOffset(); + return _gdalDatasetMetaDataCached.offset; } float GdalRawTileDataReader::depthScale() const { - return _dataset->GetRasterBand(1)->GetScale(); + return _gdalDatasetMetaDataCached.scale; } std::array GdalRawTileDataReader::getGeoTransform() const { - std::array padfTransform; - CPLErr err = _dataset->GetGeoTransform(&padfTransform[0]); - if (err == CE_Failure) { - return RawTileDataReader::getGeoTransform(); - } - return padfTransform; + return _gdalDatasetMetaDataCached.padfTransform; } IODescription GdalRawTileDataReader::getIODescription(const TileIndex& tileIndex) const { @@ -142,8 +128,7 @@ IODescription GdalRawTileDataReader::getIODescription(const TileIndex& tileIndex _initData.dimensionsWithoutPadding().x, _initData.dimensionsWithoutPadding().y); io.read.overview = 0; - io.read.fullRegion = gdalPixelRegion( - _dataset->GetRasterBand(1)); + io.read.fullRegion = fullPixelRegion(); // For correct sampling in dataset, we need to pad the texture tile PixelRegion scaledPadding = padding; @@ -169,18 +154,35 @@ void GdalRawTileDataReader::initialize() { _dataset = openGdalDataset(_datasetFilePath); // Assume all raster bands have the same data type - _gdalType = tiledatatype::getGdalDataType(_initData.glType()); + _gdalDatasetMetaDataCached.rasterCount = _dataset->GetRasterCount(); + _gdalDatasetMetaDataCached.scale = _dataset->GetRasterBand(1)->GetScale(); + _gdalDatasetMetaDataCached.offset = _dataset->GetRasterBand(1)->GetOffset(); + _gdalDatasetMetaDataCached.rasterXSize = _dataset->GetRasterXSize(); + _gdalDatasetMetaDataCached.rasterYSize = _dataset->GetRasterYSize(); + _gdalDatasetMetaDataCached.noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue(); + _gdalDatasetMetaDataCached.dataType = tiledatatype::getGdalDataType(_initData.glType()); + CPLErr err = _dataset->GetGeoTransform(&_gdalDatasetMetaDataCached.padfTransform[0]); + if (err == CE_Failure) { + _gdalDatasetMetaDataCached.padfTransform = RawTileDataReader::getGeoTransform(); + } + _depthTransform = calculateTileDepthTransform(); _cached._tileLevelDifference = calculateTileLevelDifference(_initData.dimensionsWithoutPadding().x); + + int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount(); + _cached._maxLevel = -_cached._tileLevelDifference; + if (numOverviews > 0) { + _cached._maxLevel += numOverviews - 1; + } } void GdalRawTileDataReader::readImageData( IODescription& io, RawTile::ReadError& worstError, char* imageDataDest) const { // Only read the minimum number of rasters - int nRastersToRead = std::min(_dataset->GetRasterCount(), + int nRastersToRead = std::min(_gdalDatasetMetaDataCached.rasterCount, static_cast(_initData.nRasters())); switch (_initData.ghoulTextureFormat()) { @@ -270,7 +272,8 @@ RawTile::ReadError GdalRawTileDataReader::rasterRead( dataDest += io.write.region.start.x * _initData.bytesPerPixel(); GDALRasterBand* gdalRasterBand = _dataset->GetRasterBand(rasterBand); - CPLErr readError = gdalRasterBand->RasterIO( + CPLErr readError = CE_Failure; + readError = gdalRasterBand->RasterIO( GF_Read, io.read.region.start.x, // Begin read x io.read.region.start.y, // Begin read y @@ -279,7 +282,7 @@ RawTile::ReadError GdalRawTileDataReader::rasterRead( dataDest, // Where to put data io.write.region.numPixels.x, // width to write x in destination io.write.region.numPixels.y, // width to write y in destination - _gdalType, // Type + _gdalDatasetMetaDataCached.dataType, // Type _initData.bytesPerPixel(), // Pixel spacing -io.write.bytesPerLine // Line spacing ); @@ -314,7 +317,7 @@ GDALDataset* GdalRawTileDataReader::openGdalDataset(const std::string& filePath) return dataset; } -int GdalRawTileDataReader::calculateTileLevelDifference(int minimumPixelSize) { +int GdalRawTileDataReader::calculateTileLevelDifference(int minimumPixelSize) const { GDALRasterBand* firstBand = _dataset->GetRasterBand(1); GDALRasterBand* maxOverview; int numOverviews = firstBand->GetOverviewCount(); @@ -330,52 +333,6 @@ int GdalRawTileDataReader::calculateTileLevelDifference(int minimumPixelSize) { return diff; } -bool GdalRawTileDataReader::gdalHasOverviews() const { - return _dataset->GetRasterBand(1)->GetOverviewCount() > 0; -} - -int GdalRawTileDataReader::gdalOverview( - const PixelRegion::PixelRange& regionSizeOverviewZero) const { - GDALRasterBand* firstBand = _dataset->GetRasterBand(1); - - int minNumPixels0 = glm::min(regionSizeOverviewZero.x, regionSizeOverviewZero.y); - - int overviews = firstBand->GetOverviewCount(); - GDALRasterBand* maxOverview = - overviews ? firstBand->GetOverview(overviews - 1) : firstBand; - - int sizeLevel0 = maxOverview->GetXSize(); - // The dataset itself may not have overviews but even if it does not, an overview - // for the data region can be calculated and possibly be used to sample greater - // Regions of the original dataset. - int ov = std::log2(minNumPixels0) - std::log2(sizeLevel0 + 1) - - _cached._tileLevelDifference; - ov = glm::clamp(ov, 0, overviews - 1); - - return ov; -} - -int GdalRawTileDataReader::gdalOverview(const TileIndex& tileIndex) const { - int overviews = _dataset->GetRasterBand(1)->GetOverviewCount(); - int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1); - return glm::clamp(ov, 0, overviews - 1); -} - -int GdalRawTileDataReader::gdalVirtualOverview(const TileIndex& tileIndex) const { - int overviews = _dataset->GetRasterBand(1)->GetOverviewCount(); - int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1); - return ov; -} - -PixelRegion GdalRawTileDataReader::gdalPixelRegion(GDALRasterBand* rasterBand) const { - PixelRegion gdalRegion; - gdalRegion.start.x = 0; - gdalRegion.start.y = 0; - gdalRegion.numPixels.x = rasterBand->GetXSize(); - gdalRegion.numPixels.y = rasterBand->GetYSize(); - return gdalRegion; -} - } // namespace globebrowsing } // namespace openspace diff --git a/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.h b/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.h index 109de50f3b..8280b61ec3 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.h +++ b/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.h @@ -40,7 +40,9 @@ #include #include + #include +#include class GDALDataset; class GDALRasterBand; @@ -73,7 +75,7 @@ public: // Public virtual function overloading virtual void reset() override; - virtual int maxChunkLevel() override; + virtual int maxChunkLevel() const override; virtual float noDataValueAsFloat() const override; virtual int rasterXSize() const override; virtual int rasterYSize() const override; @@ -101,19 +103,34 @@ private: // GDAL Helper methods GDALDataset* openGdalDataset(const std::string& filePath); - int calculateTileLevelDifference(int minimumPixelSize); - bool gdalHasOverviews() const; - int gdalOverview(const PixelRegion::PixelRange& baseRegionSize) const; - int gdalOverview(const TileIndex& tileIndex) const; - int gdalVirtualOverview(const TileIndex& tileIndex) const; - PixelRegion gdalPixelRegion(GDALRasterBand* rasterBand) const; + + /** + * Use as a helper function when determining the maximum tile level. This function + * returns the negated number of overviews requred to downscale the highest overview + * dataset so that it fits within minimumPixelSize pixels in the x-dimension. + */ + int calculateTileLevelDifference(int minimumPixelSize) const; // Member variables std::string _initDirectory; std::string _datasetFilePath; GDALDataset* _dataset; - GDALDataType _gdalType; + + struct GdalDatasetMetaDataCached { + int rasterCount; + float scale; + float offset; + int rasterXSize; + int rasterYSize; + float noDataValue; + std::array padfTransform; + + GDALDataType dataType; + + } _gdalDatasetMetaDataCached; + + mutable std::mutex _datasetLock; }; } // namespace globebrowsing diff --git a/modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.cpp b/modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.cpp index bfb54df704..a2f9e000b6 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.cpp +++ b/modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.cpp @@ -68,21 +68,13 @@ RawTileDataReader::RawTileDataReader(const TileTextureInitData& initData, , _hasBeenInitialized(false) {} -void RawTileDataReader::ensureInitialized() { - if (!_hasBeenInitialized) { - initialize(); - _hasBeenInitialized = true; - } -} - -std::shared_ptr RawTileDataReader::defaultTileData() { +std::shared_ptr RawTileDataReader::defaultTileData() const { return std::make_shared(RawTile::createDefault(_initData)); } std::shared_ptr RawTileDataReader::readTileData(TileIndex tileIndex, - char* dataDestination, char* pboMappedDataDestination) + char* dataDestination, char* pboMappedDataDestination) const { - ensureInitialized(); IODescription io = getIODescription(tileIndex); RawTile::ReadError worstError = RawTile::ReadError::None; @@ -136,6 +128,15 @@ const PixelRegion::PixelRange RawTileDataReader::fullPixelSize() const { return glm::uvec2(geodeticToPixel(Geodetic2(90, 180))); } +PixelRegion RawTileDataReader::fullPixelRegion() const { + PixelRegion fullRegion; + fullRegion.start.x = 0; + fullRegion.start.y = 0; + fullRegion.numPixels.x = rasterXSize(); + fullRegion.numPixels.y = rasterYSize(); + return fullRegion; +} + std::array RawTileDataReader::getGeoTransform() const { std::array padfTransform; @@ -310,9 +311,8 @@ RawTile::ReadError RawTileDataReader::repeatedRasterRead( } std::shared_ptr RawTileDataReader::getTileMetaData( - std::shared_ptr rawTile, const PixelRegion& region) + std::shared_ptr rawTile, const PixelRegion& region) const { - ensureInitialized(); size_t bytesPerLine = _initData.bytesPerPixel() * region.numPixels.x; TileMetaData* preprocessData = new TileMetaData(); diff --git a/modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h b/modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h index 58acdfdc16..4d0b5197e9 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h +++ b/modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h @@ -58,7 +58,7 @@ public: * which gets returned. */ std::shared_ptr readTileData(TileIndex tileIndex, - char* dataDestination, char* pboMappedDataDestination); + char* dataDestination, char* pboMappedDataDestination) const; TileDepthTransform getDepthTransform() const; const TileTextureInitData& tileTextureInitData() const; const PixelRegion::PixelRange fullPixelSize() const; @@ -67,7 +67,7 @@ public: * \returns the maximum chunk level available in the dataset. Should be a value * between 2 and 31. */ - virtual int maxChunkLevel() = 0; + virtual int maxChunkLevel() const = 0; /** * Reset the dataset to its initial state. This is the place to clear any cache used. @@ -78,11 +78,12 @@ public: virtual int rasterYSize() const = 0; virtual float depthOffset() const; virtual float depthScale() const; + PixelRegion fullPixelRegion() const; /** * Returns a single channeled empty RawTile of size 16 * 16 pixels. */ - std::shared_ptr defaultTileData(); + std::shared_ptr defaultTileData() const; /// Padding around all tiles to read to make sure edge blending works. const static PixelRegion padding; // same as the two above @@ -98,7 +99,7 @@ protected: /** * Call this in the constructor of the class extending RawTileDataReader */ - void ensureInitialized(); + //void ensureInitialized(); /** * The function returns a transform to map @@ -155,7 +156,7 @@ protected: int rasterBand, const IODescription& io, char* dst, int depth = 0) const; std::shared_ptr getTileMetaData( - std::shared_ptr result, const PixelRegion& region); + std::shared_ptr result, const PixelRegion& region) const; TileDepthTransform calculateTileDepthTransform(); RawTile::ReadError postProcessErrorCheck(std::shared_ptr ioResult) const; diff --git a/modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.cpp b/modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.cpp index 19907da152..e0d4d72ac7 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.cpp +++ b/modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.cpp @@ -50,14 +50,14 @@ SimpleRawTileDataReader::SimpleRawTileDataReader(const std::string& filePath, : RawTileDataReader(initData, preprocess) { _datasetFilePath = filePath; - ensureInitialized(); + initialize(); } void SimpleRawTileDataReader::reset() { initialize(); } -int SimpleRawTileDataReader::maxChunkLevel() { +int SimpleRawTileDataReader::maxChunkLevel() const { return 2; } diff --git a/modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.h b/modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.h index d0a8446d5f..0716dca804 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.h +++ b/modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.h @@ -54,7 +54,7 @@ public: // Public virtual function overloading virtual void reset() override; - virtual int maxChunkLevel() override; + virtual int maxChunkLevel() const override; virtual float noDataValueAsFloat() const override; virtual int rasterXSize() const override; virtual int rasterYSize() const override; diff --git a/modules/newhorizons/rendering/renderablefov.cpp b/modules/newhorizons/rendering/renderablefov.cpp index 33d7e51270..6e1f64d90a 100644 --- a/modules/newhorizons/rendering/renderablefov.cpp +++ b/modules/newhorizons/rendering/renderablefov.cpp @@ -653,19 +653,11 @@ void RenderableFov::computeIntercepts(const UpdateData& data, const std::string& break; } case ThisIntersect: - { - break; - } case NextIntersect: - { - break; - } case BothIntersect: - { break; - } default: - ghoul_assert(false, "Missing case label"); + throw ghoul::MissingCaseException(); } } } diff --git a/modules/onscreengui/CMakeLists.txt b/modules/onscreengui/CMakeLists.txt index eead578fc5..3a0720a818 100644 --- a/modules/onscreengui/CMakeLists.txt +++ b/modules/onscreengui/CMakeLists.txt @@ -27,6 +27,7 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/gui.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guicomponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/guifilepathcomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guihelpcomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guiorigincomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/include/guiperformancecomponent.h @@ -42,6 +43,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/gui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/gui_lua.inl ${CMAKE_CURRENT_SOURCE_DIR}/src/guicomponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guifilepathcomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guihelpcomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guiorigincomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guiperformancecomponent.cpp diff --git a/modules/onscreengui/include/gui.h b/modules/onscreengui/include/gui.h index 04d3056b16..d8a2c91dee 100644 --- a/modules/onscreengui/include/gui.h +++ b/modules/onscreengui/include/gui.h @@ -26,6 +26,7 @@ #define __OPENSPACE_MODULE_ONSCREENGUI___GUI___H__ #include +#include #include #include #include @@ -65,6 +66,7 @@ public: //protected: GuiHelpComponent _help; + GuiFilePathComponent _filePath; GuiOriginComponent _origin; GuiPerformanceComponent _performance; GuiPropertyComponent _globalProperty; diff --git a/modules/onscreengui/include/guifilepathcomponent.h b/modules/onscreengui/include/guifilepathcomponent.h new file mode 100644 index 0000000000..a859bc7bf1 --- /dev/null +++ b/modules/onscreengui/include/guifilepathcomponent.h @@ -0,0 +1,43 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_ONSCREENGUI___GUIFILEPATHCOMPONENT___H__ +#define __OPENSPACE_MODULE_ONSCREENGUI___GUIFILEPATHCOMPONENT___H__ + +#include + +namespace openspace { +namespace gui { + +class GuiFilePathComponent : public GuiComponent { +public: + GuiFilePathComponent(); + + void render() override; +}; + +} // namespace gui +} // namespace openspace + +#endif // __OPENSPACE_MODULE_ONSCREENGUI___GUIFILEPATHCOMPONENT___H__ diff --git a/modules/onscreengui/src/gui.cpp b/modules/onscreengui/src/gui.cpp index 7a44eb4583..5f91a7bfd6 100644 --- a/modules/onscreengui/src/gui.cpp +++ b/modules/onscreengui/src/gui.cpp @@ -210,12 +210,9 @@ void addScreenSpaceRenderable(std::string texturePath) { return; } - texturePath = absPath(texturePath); - texturePath = FileSys.convertPathSeparator(texturePath, '/'); - - std::string luaTable = - "{Type = 'ScreenSpaceImage', TexturePath = '" + texturePath + "' }"; - std::string script = "openspace.registerScreenSpaceRenderable(" + luaTable + ");"; + const std::string luaTable = + "{Type = 'ScreenSpaceImage', TexturePath = openspace.absPath('" + texturePath + "') }"; + const std::string script = "openspace.registerScreenSpaceRenderable(" + luaTable + ");"; OsEng.scriptEngine().queueScript(script, openspace::scripting::ScriptEngine::RemoteScripting::Yes); } } // namespace @@ -238,6 +235,7 @@ GUI::GUI() addPropertySubOwner(_property); addPropertySubOwner(_screenSpaceProperty); addPropertySubOwner(_virtualProperty); + addPropertySubOwner(_filePath); addPropertySubOwner(_time); addPropertySubOwner(_iswa); } @@ -321,6 +319,7 @@ void GUI::initialize() { _globalProperty.initialize(); _globalProperty.setHasRegularProperties(true); _virtualProperty.initialize(); + _filePath.initialize(); _performance.initialize(); _help.initialize(); _parallel.initialize(); @@ -337,6 +336,7 @@ void GUI::deinitialize() { _globalProperty.deinitialize(); _screenSpaceProperty.deinitialize(); _virtualProperty.deinitialize(); + _filePath.deinitialize(); _property.deinitialize(); delete iniFileBuffer; @@ -364,7 +364,8 @@ void GUI::initializeGL() { _fontTexture->setDataOwnership(ghoul::opengl::Texture::TakeOwnership::No); _fontTexture->uploadTexture(); GLuint id = *_fontTexture; - ImGui::GetIO().Fonts->TexID = reinterpret_cast(id); + uint64_t tmp = id; + ImGui::GetIO().Fonts->TexID = reinterpret_cast(tmp); glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); @@ -488,6 +489,9 @@ void GUI::endFrame() { if (_iswa.isEnabled()) { _iswa.render(); } + if (_filePath.isEnabled()) { + _filePath.render(); + } } ImGui::Render(); @@ -583,6 +587,10 @@ void GUI::render() { ImGui::Checkbox("Virtual Properties", &virtualProperty); _virtualProperty.setEnabled(virtualProperty); + bool filePath = _filePath.isEnabled(); + ImGui::Checkbox("File Paths", &filePath); + _filePath.setEnabled(filePath); + #ifdef OPENSPACE_MODULE_ISWA_ENABLED bool iswa = _iswa.isEnabled(); ImGui::Checkbox("iSWA", &iswa); diff --git a/modules/onscreengui/src/guifilepathcomponent.cpp b/modules/onscreengui/src/guifilepathcomponent.cpp new file mode 100644 index 0000000000..4327fc071f --- /dev/null +++ b/modules/onscreengui/src/guifilepathcomponent.cpp @@ -0,0 +1,62 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +#include "imgui.h" + +namespace openspace { +namespace gui { + +GuiFilePathComponent::GuiFilePathComponent() + : GuiComponent("File Path") +{} + +void GuiFilePathComponent::render() { + bool v = _isEnabled; + ImGui::Begin("File Path", &v); + + ImGui::Text( + "%s", + "These are file paths registered in the current OpenSpace instance." + ); + ImGui::Separator(); + + ImGui::Columns(2); + ImGui::Separator(); + std::vector tokens = FileSys.tokens(); + for (const std::string& t : tokens) { + ImGui::Text("%s", t.c_str()); + ImGui::NextColumn(); + ImGui::Text("%s", absPath(t).c_str()); + ImGui::NextColumn(); + ImGui::Separator(); + } + ImGui::End(); +} + +} // gui +} // openspace diff --git a/modules/onscreengui/src/guiiswacomponent.cpp b/modules/onscreengui/src/guiiswacomponent.cpp index b0a36ebb91..35e29c7a1d 100644 --- a/modules/onscreengui/src/guiiswacomponent.cpp +++ b/modules/onscreengui/src/guiiswacomponent.cpp @@ -38,7 +38,7 @@ namespace { using json = nlohmann::json; - const ImVec2 size = ImVec2(350, 500); + const ImVec2 WindowSize = ImVec2(350, 500); } // namespace namespace openspace { @@ -59,7 +59,7 @@ void GuiIswaComponent::render() { bool e = _isEnabled; - ImGui::Begin("ISWA", &e, size, 0.5f); + ImGui::Begin("ISWA", &e, WindowSize, 0.5f); _isEnabled = e; diff --git a/modules/space/translation/tletranslation.cpp b/modules/space/translation/tletranslation.cpp index 14e65d5b9e..b0fe0f2c3c 100644 --- a/modules/space/translation/tletranslation.cpp +++ b/modules/space/translation/tletranslation.cpp @@ -31,9 +31,11 @@ #include #include #include +#include namespace { const char* KeyFile = "File"; + const char* KeyLineNum = "LineNum"; // The list of leap years only goes until 2056 as we need to touch this file then // again anyway ;) @@ -183,7 +185,8 @@ namespace { // 3 using namespace std::chrono; int SecondsPerDay = static_cast(seconds(hours(24)).count()); - double nSecondsSince2000 = (daysSince2000 + daysInYear) * SecondsPerDay; + //Need to subtract 1 from daysInYear since it is not a zero-based count + double nSecondsSince2000 = (daysSince2000 + daysInYear - 1) * SecondsPerDay; // 4 // We need to remove additionbal leap seconds past 2000 and add them prior to @@ -244,6 +247,12 @@ documentation::Documentation TLETranslation::Documentation() { new StringVerifier, "Specifies the filename of the Two-Line-Element file", Optional::No + }, + { + KeyLineNum, + new DoubleGreaterVerifier(0), + "Specifies the line number within the file where the group of 3 TLE lines begins (1-based)", + Optional::No } }, Exhaustive::No @@ -261,10 +270,11 @@ TLETranslation::TLETranslation(const ghoul::Dictionary& dictionary) ); std::string file = dictionary.value(KeyFile); - readTLEFile(file); + int lineNum = dictionary.value(KeyLineNum); + readTLEFile(file, lineNum); } -void TLETranslation::readTLEFile(const std::string& filename) { +void TLETranslation::readTLEFile(const std::string& filename, int lineNum) { ghoul_assert(FileSys.fileExists(filename), "The filename must exist"); std::ifstream file; @@ -283,101 +293,87 @@ void TLETranslation::readTLEFile(const std::string& filename) { double epoch = 0.0; } keplerElements; - enum class State { - Initial = 0, - ReadFirstLine, - ReadSecondLine, - Finished = ReadSecondLine - }; - - State state = State::Initial; - std::string line; - while (std::getline(file, line)) { - if (line[0] == '1') { - // First line - // Field Columns Content - // 1 01-01 Line number - // 2 03-07 Satellite number - // 3 08-08 Classification (U = Unclassified) - // 4 10-11 International Designator (Last two digits of launch year) - // 5 12-14 International Designator (Launch number of the year) - // 6 15-17 International Designator(piece of the launch) A - // 7 19-20 Epoch Year(last two digits of year) - // 8 21-32 Epoch(day of the year and fractional portion of the day) - // 9 34-43 First Time Derivative of the Mean Motion divided by two - // 10 45-52 Second Time Derivative of Mean Motion divided by six - // 11 54-61 BSTAR drag term(decimal point assumed)[10] - 11606 - 4 - // 12 63-63 The "Ephemeris type" - // 13 65-68 Element set number.Incremented when a new TLE is generated - // 14 69-69 Checksum (modulo 10) - - keplerElements.epoch = epochFromSubstring(line.substr(18, 14)); - state = State::ReadFirstLine; - } - else if (line[0] == '2') { - if (state != State::ReadFirstLine) { - throw ghoul::RuntimeError( - "Malformed TLE file: '" + filename + "'. Line 2 before line 1", - "TLETranslation" - ); - } - // Second line - //Field Columns Content - // 1 01-01 Line number - // 2 03-07 Satellite number - // 3 09-16 Inclination (degrees) - // 4 18-25 Right ascension of the ascending node (degrees) - // 5 27-33 Eccentricity (decimal point assumed) - // 6 35-42 Argument of perigee (degrees) - // 7 44-51 Mean Anomaly (degrees) - // 8 53-63 Mean Motion (revolutions per day) - // 9 64-68 Revolution number at epoch (revolutions) - // 10 69-69 Checksum (modulo 10) - - std::stringstream stream; - stream.exceptions(std::ios::failbit); - - // Get inclination - stream.str(line.substr(8, 8)); - stream >> keplerElements.inclination; - stream.clear(); - - // Get Right ascension of the ascending node - stream.str(line.substr(17, 8)); - stream >> keplerElements.ascendingNode; - stream.clear(); + //Loop through and throw out lines until getting to the linNum of interest + for (unsigned int i = 1; i < lineNum; ++i) + std::getline(file, line); + std::getline(file, line); //Throw out the TLE title line (1st) - // Get Eccentricity - stream.str("0." + line.substr(26, 7)); - stream >> keplerElements.eccentricity; - stream.clear(); - - // Get argument of periapsis - stream.str(line.substr(34, 8)); - stream >> keplerElements.argumentOfPeriapsis; - stream.clear(); - - // Get mean anomaly - stream.str(line.substr(43, 8)); - stream >> keplerElements.meanAnomaly; - stream.clear(); - - // Get mean motion - stream.str(line.substr(52, 11)); - stream >> keplerElements.meanMotion; - - state = State::ReadSecondLine; - break; - } + std::getline(file, line); //Get line 1 of TLE format + if (line[0] == '1') { + // First line + // Field Columns Content + // 1 01-01 Line number + // 2 03-07 Satellite number + // 3 08-08 Classification (U = Unclassified) + // 4 10-11 International Designator (Last two digits of launch year) + // 5 12-14 International Designator (Launch number of the year) + // 6 15-17 International Designator(piece of the launch) A + // 7 19-20 Epoch Year(last two digits of year) + // 8 21-32 Epoch(day of the year and fractional portion of the day) + // 9 34-43 First Time Derivative of the Mean Motion divided by two + // 10 45-52 Second Time Derivative of Mean Motion divided by six + // 11 54-61 BSTAR drag term(decimal point assumed)[10] - 11606 - 4 + // 12 63-63 The "Ephemeris type" + // 13 65-68 Element set number.Incremented when a new TLE is generated + // 14 69-69 Checksum (modulo 10) + keplerElements.epoch = epochFromSubstring(line.substr(18, 14)); + } else { + throw ghoul::RuntimeError("File " + filename + " @ line " + + std::to_string(lineNum + 1) + " doesn't have '1' header"); } - - if (state != State::Finished) { - throw ghoul::RuntimeError( - "Malformed TLE file: Line 1 or 2 missing", - "TLETranslation" - ); + + std::getline(file, line); //Get line 2 of TLE format + if (line[0] == '2') { + // Second line + //Field Columns Content + // 1 01-01 Line number + // 2 03-07 Satellite number + // 3 09-16 Inclination (degrees) + // 4 18-25 Right ascension of the ascending node (degrees) + // 5 27-33 Eccentricity (decimal point assumed) + // 6 35-42 Argument of perigee (degrees) + // 7 44-51 Mean Anomaly (degrees) + // 8 53-63 Mean Motion (revolutions per day) + // 9 64-68 Revolution number at epoch (revolutions) + // 10 69-69 Checksum (modulo 10) + + std::stringstream stream; + stream.exceptions(std::ios::failbit); + + // Get inclination + stream.str(line.substr(8, 8)); + stream >> keplerElements.inclination; + stream.clear(); + + // Get Right ascension of the ascending node + stream.str(line.substr(17, 8)); + stream >> keplerElements.ascendingNode; + stream.clear(); + + // Get Eccentricity + stream.str("0." + line.substr(26, 7)); + stream >> keplerElements.eccentricity; + stream.clear(); + + // Get argument of periapsis + stream.str(line.substr(34, 8)); + stream >> keplerElements.argumentOfPeriapsis; + stream.clear(); + + // Get mean anomaly + stream.str(line.substr(43, 8)); + stream >> keplerElements.meanAnomaly; + stream.clear(); + + // Get mean motion + stream.str(line.substr(52, 11)); + stream >> keplerElements.meanMotion; + } else { + throw ghoul::RuntimeError("File " + filename + " @ line " + + std::to_string(lineNum + 2) + " doesn't have '2' header"); } + file.close(); // Calculate the semi major axis based on the mean motion using kepler's laws keplerElements.semiMajorAxis = calculateSemiMajorAxis(keplerElements.meanMotion); diff --git a/modules/space/translation/tletranslation.h b/modules/space/translation/tletranslation.h index 730fbbe677..a720b8127a 100644 --- a/modules/space/translation/tletranslation.h +++ b/modules/space/translation/tletranslation.h @@ -38,6 +38,11 @@ namespace openspace { */ class TLETranslation : public KeplerTranslation { public: + struct FileFormatError : public ghoul::RuntimeError { + explicit FileFormatError(std::string offense); + std::string offense; + }; + /** * Constructor for the TLETranslation class. The \p dictionary must contain a key for * the file that contains the TLE information. The ghoul::Dictionary will be tested @@ -61,13 +66,14 @@ private: * disallowed values (see KeplerTranslation::setKeplerElements), a * KeplerTranslation::RangeError is thrown. * \param filename The path to the file that contains the TLE file. + * \param lineNum The line number in the file where the set of 3 TLE lines starts * \throw std::system_error if the TLE file is malformed (does not contain at least * two lines that start with \c 1 and \c 2. * \throw KeplerTranslation::RangeError If the Keplerian elements are outside of * the valid range supported by Kepler::setKeplerElements * \pre The \p filename must exist */ - void readTLEFile(const std::string& filename); + void readTLEFile(const std::string& filename, int lineNum); }; } // namespace openspace diff --git a/modules/toyvolume/rendering/renderabletoyvolume.cpp b/modules/toyvolume/rendering/renderabletoyvolume.cpp index e5df97b9cf..8086a47ac5 100644 --- a/modules/toyvolume/rendering/renderabletoyvolume.cpp +++ b/modules/toyvolume/rendering/renderabletoyvolume.cpp @@ -41,8 +41,8 @@ RenderableToyVolume::RenderableToyVolume(const ghoul::Dictionary& dictionary) , _stepSize("stepSize", "Step Size", 0.02, 0.01, 1) , _scaling("scaling", "Scaling", glm::vec3(1.0, 1.0, 1.0), glm::vec3(0.0), glm::vec3(10.0)) , _translation("translation", "Translation", glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0), glm::vec3(10.0)) - , _rotation("rotation", "Euler rotation", glm::vec3(0.0, 0.0, 0.0), glm::vec3(0), glm::vec3(6.28)) - , _color("color", "Color", glm::vec4(1.0, 0.0, 0.0, 0.1), glm::vec4(0.0), glm::vec4(1.0)) { + , _rotation("rotation", "Euler rotation", glm::vec3(0.f, 0.f, 0.f), glm::vec3(0), glm::vec3(6.28f)) + , _color("color", "Color", glm::vec4(1.f, 0.f, 0.f, 0.1f), glm::vec4(0.f), glm::vec4(1.f)) { float scalingExponent, stepSize; glm::vec3 scaling, translation, rotation; diff --git a/openspace.cfg b/openspace.cfg index be2b51159c..07395a1997 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -9,8 +9,6 @@ return { -- A regular 1280x720 window --SGCTConfig = sgct.config.single{}, - -- SGCTConfig = sgct.config.single{res={1920, 1080}, shared=true}, - -- A regular 1920x1080 window SGCTConfig = sgct.config.single{1920, 1080}, @@ -20,6 +18,10 @@ return { -- A 4k fisheye rendering in a 1024x1024 window --SGCTConfig = sgct.config.fisheye{1024, 1024, res={4096, 4096}, quality="2k", tilt=27}, + -- Streaming OpenSpace via Spout to OBS + -- SGCTConfig = sgct.config.single{2560, 1440, shared=true, name="WV_OBS_SPOUT1"}, + + --SGCTConfig = "${SGCT}/openvr_oculusRiftCv1.xml", --SGCTConfig = "${SGCT}/openvr_htcVive.xml", @@ -78,7 +80,7 @@ return { -- OnScreenTextScaling = "framebuffer", -- PerSceneCache = true, -- DisableRenderingOnMaster = true, - KeyDisableSceneOnMaster = true, + -- DisableSceneOnMaster = true, DownloadRequestURL = "http://data.openspaceproject.com/request.cgi", RenderingMethod = "Framebuffer", OpenGLDebugContext = { diff --git a/scripts/bind_keys_satellites.lua b/scripts/bind_keys_satellites.lua new file mode 100644 index 0000000000..c2d53dbd7c --- /dev/null +++ b/scripts/bind_keys_satellites.lua @@ -0,0 +1,23 @@ +--[[ OpenSpace keybinding script loaded from the satellites.scene file ]]-- + +-- Load the common helper functions +dofile(openspace.absPath('${SCRIPTS}/common.lua')) +dofile(openspace.absPath('${SCRIPTS}/bind_common_keys.lua')) + +-- Set focuses + +openspace.bindKey( + "p" , + "if gpsVis then gpsVis = false; else gpsVis = true; end; openspace.setPropertyValue('gps-ops*.renderable.enabled', not gpsVis)", + "Toggles visibility of gps satellites." +) +openspace.bindKey( + "s" , + "if stVis then stVis = false; else stVis = true; end; openspace.setPropertyValue('station*.renderable.enabled', not stVis)", + "Toggles visibility of stations." +) +openspace.bindKey( + "g" , + "if geoVis then geoVis = false; else geoVis = true; end; openspace.setPropertyValue('geo*.renderable.enabled', not geoVis)", + "Toggles visibility of geostationary." +) \ No newline at end of file diff --git a/src/documentation/documentation.cpp b/src/documentation/documentation.cpp index bef1b621d4..e82cea9388 100644 --- a/src/documentation/documentation.cpp +++ b/src/documentation/documentation.cpp @@ -80,7 +80,8 @@ std::string to_string(openspace::documentation::TestResult::Offense::Reason reas case openspace::documentation::TestResult::Offense::Reason::WrongType: return "Wrong type"; default: - ghoul_assert(false, "Missing case label"); + throw ghoul::MissingCaseException(); + } } @@ -89,7 +90,7 @@ std::string to_string(openspace::documentation::TestResult::Warning::Reason reas case openspace::documentation::TestResult::Warning::Reason::Deprecated: return "Deprecated"; default: - ghoul_assert(false, "Missing case label"); + throw ghoul::MissingCaseException(); } } diff --git a/src/documentation/documentationengine.cpp b/src/documentation/documentationengine.cpp index 92f48e26ae..4f862869fc 100644 --- a/src/documentation/documentationengine.cpp +++ b/src/documentation/documentationengine.cpp @@ -298,6 +298,7 @@ std::string DocumentationEngine::generateJson() const { void DocumentationEngine::addDocumentation(Documentation doc) { for (const DocumentationEntry& e : doc.entries) { + (void)e; // Unused variable in Release mode ghoul_assert( e.documentation.find('"') == std::string::npos, "Documentation cannot contain \" character" diff --git a/src/documentation/documentationgenerator.cpp b/src/documentation/documentationgenerator.cpp index ac27f90b67..3f11f02563 100644 --- a/src/documentation/documentationgenerator.cpp +++ b/src/documentation/documentationgenerator.cpp @@ -52,6 +52,7 @@ DocumentationGenerator::DocumentationGenerator(std::string name, ghoul_precondition(!_name.empty(), "name must not be empty"); ghoul_precondition(!_jsonName.empty(), "jsonName must not be empty"); for (const HandlebarTemplate& t : _handlebarTemplates) { + (void)t; // Unused variable in Release mode ghoul_precondition(!t.name.empty(), "name must not be empty"); ghoul_precondition(!t.filename.empty(), "filename must not be empty"); } diff --git a/src/engine/downloadmanager.cpp b/src/engine/downloadmanager.cpp index af894ff333..d1e205788a 100644 --- a/src/engine/downloadmanager.cpp +++ b/src/engine/downloadmanager.cpp @@ -158,6 +158,7 @@ DownloadManager::DownloadManager(std::string requestURL, int applicationVersion, std::shared_ptr DownloadManager::downloadFile( const std::string& url, const ghoul::filesystem::File& file, bool overrideFile, + bool failOnError, unsigned int timeout_secs, DownloadFinishedCallback finishedCallback, DownloadProgressCallback progressCallback) { if (!overrideFile && FileSys.fileExists(file)) @@ -168,27 +169,39 @@ std::shared_ptr DownloadManager::downloadFile( #ifdef WIN32 FILE* fp; errno_t error = fopen_s(&fp, file.path().c_str(), "wb"); - ghoul_assert( - error == 0, - "Could not open/create file:" + file.path() + ". Errno: " + std::to_string(errno) - ); + if (error != 0) { + LERROR( + "Could not open/create file:" + file.path() + + ". Errno: " + std::to_string(errno) + ); + } #else FILE* fp = fopen(file.path().c_str(), "wb"); // write binary #endif // WIN32 - ghoul_assert( - fp != nullptr, - "Could not open/create file:" + file.path() + ". Errno: " + std::to_string(errno) - ); - - //LDEBUG("Start downloading file: '" << url << "' into file '" << file.path() << "'"); + if (!fp) { + LERROR( + "Could not open/create file:" + file.path() + + ". Errno: " + std::to_string(errno) + ); + } - auto downloadFunction = [url, finishedCallback, progressCallback, future, fp]() { + auto downloadFunction = [url, + failOnError, + timeout_secs, + finishedCallback, + progressCallback, + future, + fp]() { CURL* curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); + if (timeout_secs) + curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout_secs); + if (failOnError) + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); ProgressInformation p = { future, @@ -337,6 +350,8 @@ std::vector> DownloadManager::downl line, destination.path() + "/" + file, overrideFiles, + false, + 0, [](const FileFuture& f) { LDEBUG("Finished: " << f.filePath); } ); if (future) @@ -349,6 +364,8 @@ std::vector> DownloadManager::downl fullRequest, requestFile, true, + false, + 0, callback ); diff --git a/src/engine/logfactory.cpp b/src/engine/logfactory.cpp index cddfd946b2..01366f9c21 100644 --- a/src/engine/logfactory.cpp +++ b/src/engine/logfactory.cpp @@ -212,7 +212,9 @@ std::unique_ptr createLog(const ghoul::Dictionary& dictiona ); } } - ghoul_assert(false, "Missing case in the documentation for LogFactory"); + else { + throw ghoul::MissingCaseException(); + } } } // namespace openspace diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 11de8ea7ab..81318c1d5a 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -953,7 +953,7 @@ void OpenSpaceEngine::initializeGL() { LDEBUGC(category, std::string(message)); break; default: - ghoul_assert(false, "Missing case label"); + throw ghoul::MissingCaseException(); } }; ghoul::opengl::debug::setDebugCallback(callback); @@ -1242,6 +1242,12 @@ scripting::LuaLibrary OpenSpaceEngine::luaLibrary() { "", "Writes out documentation files" }, + { + "downloadFile", + &luascriptfunctions::downloadFile, + "", + "Downloads a file from Lua scope" + }, { "addVirtualProperty", &luascriptfunctions::addVirtualProperty, @@ -1302,7 +1308,7 @@ void OpenSpaceEngine::registerModuleCallback(OpenSpaceEngine::CallbackOption opt _moduleCallbacks.postDraw.push_back(std::move(function)); break; default: - ghoul_assert(false, "Missing case label"); + throw ghoul::MissingCaseException(); } } diff --git a/src/engine/openspaceengine_lua.inl b/src/engine/openspaceengine_lua.inl index ad48ed9751..cd2695a65b 100644 --- a/src/engine/openspaceengine_lua.inl +++ b/src/engine/openspaceengine_lua.inl @@ -139,6 +139,31 @@ int removeAllVirtualProperties(lua_State* L) { return 0; } +/** +* \ingroup LuaScripts +* downloadFile(): +* Downloads a file from Lua interpreter +*/ +int downloadFile(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 2) + return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); + std::string uri = luaL_checkstring(L, -2); + std::string savePath = luaL_checkstring(L, -1); + + const std::string _loggerCat = "OpenSpaceEngine"; + LINFO("Downloading file from " << uri); + DownloadManager dm = openspace::DownloadManager("", 1, false); + std::shared_ptr future = + dm.downloadFile(uri, absPath("${SCENE}/" + savePath), true, true, 5); + if (!future || (future && !future->isFinished)) { + std::string errorMsg = "Download failed"; + if (future) + errorMsg += ": " + future->errorMessage; + return luaL_error(L, errorMsg.c_str()); + } + return 1; +} } // namespace luascriptfunctions } // namespace openspace diff --git a/src/engine/wrapper/sgctwindowwrapper.cpp b/src/engine/wrapper/sgctwindowwrapper.cpp index 2ea95e707a..8d04c9aa98 100644 --- a/src/engine/wrapper/sgctwindowwrapper.cpp +++ b/src/engine/wrapper/sgctwindowwrapper.cpp @@ -158,6 +158,7 @@ int SGCTWindowWrapper::currentNumberOfAaSamples() const { bool SGCTWindowWrapper::isRegularRendering() const { sgct::SGCTWindow* w = sgct::Engine::instance()->getCurrentWindowPtr(); std::size_t nViewports = w->getNumberOfViewports(); + (void)nViewports; // Unused in Release mode ghoul_assert(nViewports > 0, "At least one viewport must exist at this time"); sgct_core::Viewport* vp = w->getViewport(0); sgct_core::NonLinearProjection* nlp = vp->getNonLinearProjectionPtr(); diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index 0a73c4eb12..85db183e4f 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -94,9 +94,9 @@ ParallelConnection::ParallelConnection() , _address("address", "Address", "localhost") , _name("name", "Connection name", "Anonymous") , _bufferTime("bufferTime", "Buffer Time", 1, 0.5, 10) - , _timeKeyframeInterval("timeKeyframeInterval", "Time keyframe interval", 0.1, 0, 1) - , _cameraKeyframeInterval("cameraKeyframeInterval", "Camera Keyframe interval", 0.1, 0, 1) - , _timeTolerance("timeTolerance", "Time tolerance", 1, 0.5, 5) + , _timeKeyframeInterval("timeKeyframeInterval", "Time keyframe interval", 0.1f, 0.f, 1.f) + , _cameraKeyframeInterval("cameraKeyframeInterval", "Camera Keyframe interval", 0.1f, 0.f, 1.f) + , _timeTolerance("timeTolerance", "Time tolerance", 1.f, 0.5f, 5.f) , _lastTimeKeyframeTimestamp(0) , _lastCameraKeyframeTimestamp(0) , _clientSocket(INVALID_SOCKET) diff --git a/src/rendering/renderable.cpp b/src/rendering/renderable.cpp index 231bbcbb0f..b8b926a5fa 100644 --- a/src/rendering/renderable.cpp +++ b/src/rendering/renderable.cpp @@ -70,9 +70,11 @@ std::unique_ptr Renderable::createFromDictionary( const ghoul::Dictionary& dictionary) { // The name is passed down from the SceneGraphNode - std::string name; - bool success = dictionary.getValue(SceneGraphNode::KeyName, name); - ghoul_assert(success, "The SceneGraphNode did not set the 'name' key"); + ghoul_assert( + dictionary.hasKeyAndValue(SceneGraphNode::KeyName), + "The SceneGraphNode did not set the 'name' key" + ); + std::string name = dictionary.value(SceneGraphNode::KeyName); documentation::testSpecificationAndThrow(Documentation(), dictionary, "Renderable"); diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index 31554ac1af..ee86083384 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -179,7 +179,9 @@ int property_setValueSingle(lua_State* L) { for (properties::Property* prop : allProperties()) { std::string propFullId = prop->fullyQualifiedIdentifier(); //Look for a match in the uri with the group name (first term) removed - int propMatchLength = propFullId.length() - pathRemainderToMatch.length(); + int propMatchLength = + static_cast(propFullId.length()) - + static_cast(pathRemainderToMatch.length()); if (propMatchLength >= 0) { std::string thisPropMatchId = propFullId.substr(propMatchLength); @@ -289,7 +291,7 @@ int property_setValueRegex(lua_State* L) { } catch (const std::regex_error& e) { LERRORC("property_setValueRegex", "Malformed regular expression: '" - << regex << "'"); + << regex << "':" << e.what()); } return 0; diff --git a/src/scripting/scriptengine_lua.inl b/src/scripting/scriptengine_lua.inl index a65f70f3d4..0eda39fd16 100644 --- a/src/scripting/scriptengine_lua.inl +++ b/src/scripting/scriptengine_lua.inl @@ -200,6 +200,7 @@ int absolutePath(lua_State* L) { std::string path = luaL_checkstring(L, -1); path = absPath(path); + path = FileSys.convertPathSeparator(path, '/'); lua_pushstring(L, path.c_str()); return 1; } diff --git a/src/util/keys.cpp b/src/util/keys.cpp index 056bd85bd9..ab02ed8bc4 100644 --- a/src/util/keys.cpp +++ b/src/util/keys.cpp @@ -129,7 +129,7 @@ std::string to_string(openspace::Key key) { return p.first; } } - ghoul_assert(false, "Missing key in KeyMapping"); + throw ghoul::MissingCaseException(); } std::string to_string(openspace::KeyModifier mod) { diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index 05fb0cc8f2..d73edaec64 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -75,7 +75,8 @@ namespace { case openspace::SpiceManager::FieldOfViewMethod::Point: return "POINT"; default: - ghoul_assert(false, "Missing case label"); + throw ghoul::MissingCaseException(); + } } @@ -86,7 +87,8 @@ namespace { case openspace::SpiceManager::TerminatorType::Penumbral: return "PENUMBRAL"; default: - ghoul_assert(false, "Missing case label"); + throw ghoul::MissingCaseException(); + } } } @@ -148,7 +150,7 @@ SpiceManager::AberrationCorrection::operator const char*() const { case Type::ConvergedNewtonianStellar: return (direction == Direction::Reception) ? "CN+S" : "XCN+S"; default: - ghoul_assert(false, "Missing case label"); + throw ghoul::MissingCaseException(); } } diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 82e00c7651..237e677b61 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -247,7 +247,7 @@ function (handle_applications) if (WIN32) - copy_files( + ghl_copy_files( ${APPLICATION_NAME} "${CURL_ROOT_DIR}/lib/libcurl.dll" "${CURL_ROOT_DIR}/lib/libeay32.dll" @@ -293,7 +293,7 @@ function (handle_option_vld) target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) foreach (app ${OPENSPACE_APPLCATIONS}) - copy_files(${app} "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll") + ghl_copy_files(${app} "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll") endforeach () endif () endfunction () @@ -503,7 +503,7 @@ function (handle_internal_modules) if (WIN32) foreach (application ${OPENSPACE_APPLICATIONS}) foreach (dll ${dll_list}) - copy_files(${application} ${dll}) + ghl_copy_files(${application} ${dll}) endforeach () endforeach () endif () @@ -511,7 +511,7 @@ endfunction () function (copy_dynamic_libraries) if (WIN32) - copy_files(OpenSpace "${CURL_ROOT_DIR}/lib/libcurl.dll") + ghl_copy_files(OpenSpace "${CURL_ROOT_DIR}/lib/libcurl.dll") # Copy DLLs needed by Ghoul into the executable directory ghl_copy_shared_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/ghoul)