mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-08 07:28:22 -10:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7040c4c17e | ||
|
|
7727b00f76 | ||
|
|
24b15298b6 | ||
|
|
26dcfd9fbb |
No files matched your search
@@ -217,6 +217,7 @@ Flag exe_params[] =
|
||||
{ "-dis_weapons", "Disable weapon rendering", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-dis_weapons", },
|
||||
{ "-output_sexps", "Output SEXPs to sexps.html", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_sexps", },
|
||||
{ "-output_scripting", "Output scripting to scripting.html", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_scripting", },
|
||||
{ "-output_json_data", "Output table objects to [table].json", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_json_data", },
|
||||
{ "-save_render_target", "Save render targets to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-save_render_target", },
|
||||
{ "-debug_window", "Display debug window", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-debug_window", },
|
||||
{ "-verify_vps", "Spew VP CRCs to vp_crcs.txt", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-verify_vps", },
|
||||
@@ -494,6 +495,9 @@ bool Cmdline_no_unfocus_pause = false;
|
||||
cmdline_parm get_flags_arg("-get_flags", "Output the launcher flags file", AT_NONE);
|
||||
cmdline_parm output_sexp_arg("-output_sexps", NULL, AT_NONE); //WMC - outputs all SEXPs to sexps.html
|
||||
cmdline_parm output_scripting_arg("-output_scripting", NULL, AT_NONE); //WMC
|
||||
cmdline_parm output_json_data_arg("-output_json_data", "Output table objects to [table].json", AT_NONE); //chief1983, Cmdline_output_json_data
|
||||
|
||||
bool Cmdline_output_json_data = false;
|
||||
|
||||
// Deprecated flags - CommanderDJ
|
||||
cmdline_parm deprecated_spec_arg("-spec", "Deprecated", AT_NONE);
|
||||
@@ -1509,6 +1513,11 @@ bool SetCmdlineParams()
|
||||
output_sexps("sexps.html");
|
||||
}
|
||||
|
||||
if ( output_json_data_arg.found() )
|
||||
{
|
||||
Cmdline_output_json_data = true;
|
||||
}
|
||||
|
||||
if ( no_vbo_arg.found() )
|
||||
{
|
||||
Cmdline_novbo = 1;
|
||||
|
||||
@@ -155,5 +155,6 @@ extern int Cmdline_reparse_mainhall;
|
||||
extern bool Cmdline_frame_profile;
|
||||
extern bool Cmdline_profile_write_file;
|
||||
extern bool Cmdline_no_unfocus_pause;
|
||||
extern bool Cmdline_output_json_data;
|
||||
|
||||
#endif
|
||||
@@ -7127,6 +7127,13 @@ int game_main(char *cmdline)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// maybe output parsed table objects, and exit
|
||||
if (Cmdline_output_json_data) {
|
||||
ship_output_json("ships.json");
|
||||
weapons_output_json("weapons.json");
|
||||
game_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// maybe spew VP CRCs, and exit
|
||||
if (Cmdline_verify_vps) {
|
||||
|
||||
+488
-158
@@ -83,7 +83,17 @@
|
||||
#include "debugconsole/console.h"
|
||||
#include "debugconsole/console.h"
|
||||
#include "math/vecmat.h"
|
||||
#include "cfile/cfilesystem.h"
|
||||
|
||||
#include "jansson.h"
|
||||
|
||||
#ifndef JSON_REAL_PRECISION
|
||||
#define JSON_REAL_PRECISION(n) 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_ESCAPE_SLASH
|
||||
#define JSON_ESCAPE_SLASH 0
|
||||
#endif
|
||||
|
||||
#define NUM_SHIP_SUBSYSTEM_SETS 20 // number of subobject sets to use (because of the fact that it's a linked list,
|
||||
// we can't easily go fully dynamic)
|
||||
@@ -1256,6 +1266,60 @@ void parse_ship_particle_effect(ship_info* sip, particle_effect* pe, char *id_st
|
||||
}
|
||||
}
|
||||
|
||||
void parse_allowed_weapons(ship_info *sip, bool is_primary, bool is_dogfight)
|
||||
{
|
||||
int i, num_allowed;
|
||||
int allowed_weapons[MAX_WEAPON_TYPES];
|
||||
const int max_banks = (is_primary ? MAX_SHIP_PRIMARY_BANKS : MAX_SHIP_SECONDARY_BANKS);
|
||||
const int weapon_type = (is_dogfight ? DOGFIGHT_WEAPON : REGULAR_WEAPON);
|
||||
const int offset = (is_primary ? 0 : MAX_SHIP_PRIMARY_BANKS);
|
||||
const char *allowed_banks_str = is_primary ? (is_dogfight ? "$Allowed Dogfight PBanks:" : "$Allowed PBanks:")
|
||||
: (is_dogfight ? "$Allowed Dogfight SBanks:" : "$Allowed SBanks:");
|
||||
const char *bank_type_str = is_primary ? "primary" : "secondary";
|
||||
|
||||
// Goober5000 - fixed Bobboau's implementation of restricted banks
|
||||
int bank;
|
||||
|
||||
// Set the weapons filter used in weapons loadout (for primary weapons)
|
||||
if (optional_string(allowed_banks_str))
|
||||
{
|
||||
bank = -1;
|
||||
|
||||
while (check_for_string("("))
|
||||
{
|
||||
bank++;
|
||||
|
||||
// make sure we don't specify more than we have banks for
|
||||
if (bank >= max_banks)
|
||||
{
|
||||
Warning(LOCATION, "%s bank-specific loadout for %s exceeds permissible number of %s banks. Ignoring the rest...", allowed_banks_str, sip->name, bank_type_str);
|
||||
bank--;
|
||||
break;
|
||||
}
|
||||
|
||||
num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
|
||||
|
||||
// actually say which weapons are allowed
|
||||
for ( i = 0; i < num_allowed; i++ )
|
||||
{
|
||||
if ( allowed_weapons[i] >= 0 ) // MK, Bug fix, 9/6/99. Used to be "allowed_weapons" not "allowed_weapons[i]".
|
||||
{
|
||||
sip->allowed_bank_restricted_weapons[offset+bank][allowed_weapons[i]] |= weapon_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set flags if need be
|
||||
if (bank > 0) // meaning there was a restricted bank table entry
|
||||
{
|
||||
for (i=0; i<=bank; i++)
|
||||
{
|
||||
sip->restricted_loadout_flag[offset+i] |= weapon_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method for parsing ship/subsystem primary/secondary weapons so that the parser doesn't flip out in the event of a problem.
|
||||
*
|
||||
@@ -1355,8 +1419,7 @@ int parse_and_add_briefing_icon_info()
|
||||
int parse_ship_values(ship_info* sip, bool first_time, bool replace)
|
||||
{
|
||||
char buf[SHIP_MULTITEXT_LENGTH];
|
||||
int i, j, num_allowed;
|
||||
int allowed_weapons[MAX_WEAPON_TYPES];
|
||||
int i, j;
|
||||
int rtn = 0;
|
||||
char name_tmp[NAME_LENGTH];
|
||||
|
||||
@@ -1628,7 +1691,7 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace)
|
||||
if (optional_string("$Enable Team Colors:")) {
|
||||
stuff_boolean(&sip->uses_team_colors);
|
||||
sip->default_team_name = "None";
|
||||
}
|
||||
}
|
||||
|
||||
if (optional_string("$Default Team:")) {
|
||||
char temp[NAME_LENGTH];
|
||||
@@ -2221,86 +2284,9 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace)
|
||||
stuff_float( &sip->weapon_model_draw_distance );
|
||||
}
|
||||
|
||||
// Goober5000 - fixed Bobboau's implementation of restricted banks
|
||||
int bank;
|
||||
|
||||
// Set the weapons filter used in weapons loadout (for primary weapons)
|
||||
if (optional_string("$Allowed PBanks:"))
|
||||
{
|
||||
bank = -1;
|
||||
|
||||
while (check_for_string("("))
|
||||
{
|
||||
bank++;
|
||||
|
||||
// make sure we don't specify more than we have banks for
|
||||
if (bank >= MAX_SHIP_PRIMARY_BANKS)
|
||||
{
|
||||
Warning(LOCATION, "$Allowed PBanks bank-specific loadout for %s exceeds permissible number of primary banks. Ignoring the rest...", sip->name);
|
||||
bank--;
|
||||
break;
|
||||
}
|
||||
|
||||
num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
|
||||
|
||||
// actually say which weapons are allowed
|
||||
for ( i = 0; i < num_allowed; i++ )
|
||||
{
|
||||
if ( allowed_weapons[i] >= 0 ) // MK, Bug fix, 9/6/99. Used to be "allowed_weapons" not "allowed_weapons[i]".
|
||||
{
|
||||
sip->allowed_bank_restricted_weapons[bank][allowed_weapons[i]] |= REGULAR_WEAPON;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set flags if need be
|
||||
if (bank > 0) // meaning there was a restricted bank table entry
|
||||
{
|
||||
for (i=0; i<=bank; i++)
|
||||
{
|
||||
sip->restricted_loadout_flag[i] |= REGULAR_WEAPON;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the weapons filter used in weapons loadout (for primary weapons)
|
||||
if (optional_string("$Allowed Dogfight PBanks:"))
|
||||
{
|
||||
bank = -1;
|
||||
|
||||
while (check_for_string("("))
|
||||
{
|
||||
bank++;
|
||||
|
||||
// make sure we don't specify more than we have banks for
|
||||
if (bank >= MAX_SHIP_PRIMARY_BANKS)
|
||||
{
|
||||
Warning(LOCATION, "$Allowed Dogfight PBanks bank-specific loadout for %s exceeds permissible number of primary banks. Ignoring the rest...", sip->name);
|
||||
bank--;
|
||||
break;
|
||||
}
|
||||
|
||||
num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
|
||||
|
||||
// actually say which weapons are allowed
|
||||
for ( i = 0; i < num_allowed; i++ )
|
||||
{
|
||||
if ( allowed_weapons[i] >= 0 ) // MK, Bug fix, 9/6/99. Used to be "allowed_weapons" not "allowed_weapons[i]".
|
||||
{
|
||||
sip->allowed_bank_restricted_weapons[bank][allowed_weapons[i]] |= DOGFIGHT_WEAPON;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set flags if need be
|
||||
if (bank > 0) // meaning there was a restricted bank table entry
|
||||
{
|
||||
for (i=0; i<=bank; i++)
|
||||
{
|
||||
sip->restricted_loadout_flag[i] |= DOGFIGHT_WEAPON;
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_allowed_weapons(sip, true, false);
|
||||
parse_allowed_weapons(sip, true, true);
|
||||
|
||||
// Get primary bank weapons
|
||||
parse_weapon_bank(sip, true, &sip->num_primary_banks, sip->primary_bank_weapons, sip->primary_bank_ammo_capacity);
|
||||
@@ -2312,82 +2298,8 @@ int parse_ship_values(ship_info* sip, bool first_time, bool replace)
|
||||
}
|
||||
|
||||
// Set the weapons filter used in weapons loadout (for secondary weapons)
|
||||
if (optional_string("$Allowed SBanks:"))
|
||||
{
|
||||
bank = -1;
|
||||
|
||||
while (check_for_string("("))
|
||||
{
|
||||
bank++;
|
||||
|
||||
// make sure we don't specify more than we have banks for
|
||||
if (bank >= MAX_SHIP_SECONDARY_BANKS)
|
||||
{
|
||||
Warning(LOCATION, "$Allowed SBanks bank-specific loadout for %s exceeds permissible number of secondary banks. Ignoring the rest...", sip->name);
|
||||
bank--;
|
||||
break;
|
||||
}
|
||||
|
||||
num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
|
||||
|
||||
// actually say which weapons are allowed
|
||||
for ( i = 0; i < num_allowed; i++ )
|
||||
{
|
||||
if ( allowed_weapons[i] >= 0 ) // MK, Bug fix, 9/6/99. Used to be "allowed_weapons" not "allowed_weapons[i]".
|
||||
{
|
||||
sip->allowed_bank_restricted_weapons[MAX_SHIP_PRIMARY_BANKS+bank][allowed_weapons[i]] |= REGULAR_WEAPON;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set flags if need be
|
||||
if (bank > 0) // meaning there was a restricted bank table entry
|
||||
{
|
||||
for (i=0; i<=bank; i++)
|
||||
{
|
||||
sip->restricted_loadout_flag[MAX_SHIP_PRIMARY_BANKS+i] |= REGULAR_WEAPON;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the weapons filter used in weapons loadout (for secondary weapons)
|
||||
if (optional_string("$Allowed Dogfight SBanks:"))
|
||||
{
|
||||
bank = -1;
|
||||
|
||||
while (check_for_string("("))
|
||||
{
|
||||
bank++;
|
||||
|
||||
// make sure we don't specify more than we have banks for
|
||||
if (bank >= MAX_SHIP_SECONDARY_BANKS)
|
||||
{
|
||||
Warning(LOCATION, "$Allowed Dogfight SBanks bank-specific loadout for %s exceeds permissible number of secondary banks. Ignoring the rest...", sip->name);
|
||||
bank--;
|
||||
break;
|
||||
}
|
||||
|
||||
num_allowed = stuff_int_list(allowed_weapons, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
|
||||
|
||||
// actually say which weapons are allowed
|
||||
for ( i = 0; i < num_allowed; i++ )
|
||||
{
|
||||
if ( allowed_weapons[i] >= 0 ) // MK, Bug fix, 9/6/99. Used to be "allowed_weapons" not "allowed_weapons[i]".
|
||||
{
|
||||
sip->allowed_bank_restricted_weapons[MAX_SHIP_PRIMARY_BANKS+bank][allowed_weapons[i]] |= DOGFIGHT_WEAPON;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set flags if need be
|
||||
if (bank > 0) // meaning there was a restricted bank table entry
|
||||
{
|
||||
for (i=0; i<=bank; i++)
|
||||
{
|
||||
sip->restricted_loadout_flag[MAX_SHIP_PRIMARY_BANKS+i] |= DOGFIGHT_WEAPON;
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_allowed_weapons(sip, false, false);
|
||||
parse_allowed_weapons(sip, false, true);
|
||||
|
||||
// Get secondary bank weapons
|
||||
parse_weapon_bank(sip, false, &sip->num_secondary_banks, sip->secondary_bank_weapons, sip->secondary_bank_ammo_capacity);
|
||||
@@ -17888,3 +17800,421 @@ int get_nearest_bbox_point(object *ship_obj, vec3d *start, vec3d *box_pt)
|
||||
|
||||
return inside;
|
||||
}
|
||||
|
||||
bool check_restricted(const ship_info* sip, json_t* bank_json, bool restricted, int bank, int weapon_idx, int weapon_type)
|
||||
{
|
||||
// bank is a bank index, weapon_idx is a weapon type. sip->allowed_bank_restricted_weapons[bank][weapon_idx] is a 2-bit bitfield.
|
||||
if(sip->allowed_bank_restricted_weapons[bank][weapon_idx] & weapon_type)
|
||||
{
|
||||
json_array_append(bank_json, json_string(Weapon_info[weapon_idx].name));
|
||||
if(!restricted && bank > 0)
|
||||
{
|
||||
restricted = true;
|
||||
}
|
||||
}
|
||||
|
||||
return restricted;
|
||||
}
|
||||
|
||||
void append_weapons(const ship_info* sip, json_t* ship_data, bool is_primary, int bank_offset)
|
||||
{
|
||||
int bank, weapon_idx, num_banks;
|
||||
bool restricted = false, dogfight_restricted = false;
|
||||
json_t *Allowed_banks = json_array();
|
||||
json_t *Allowed_dogfight_banks = json_array();
|
||||
json_t *Default_banks = json_array();
|
||||
json_t *Bank_capacity = json_array();
|
||||
json_t *Show_models = json_array();
|
||||
char Allowed_Banks_name[16], Allowed_Dogfight_Banks_name[25], Default_Banks_name[16], Bank_Capacity_name[16], Show_Models_name[23], type_name[10];
|
||||
|
||||
if(is_primary)
|
||||
{
|
||||
num_banks = sip->num_primary_banks;
|
||||
strcpy_s(type_name, "Primary");
|
||||
}
|
||||
else
|
||||
{
|
||||
num_banks = sip->num_secondary_banks;
|
||||
strcpy_s(type_name, "Secondary");
|
||||
}
|
||||
sprintf(Allowed_Banks_name, "$Allowed %cBanks", type_name[0]);
|
||||
sprintf(Allowed_Dogfight_Banks_name, "$Allowed Dogfight %cBanks", type_name[0]);
|
||||
sprintf(Default_Banks_name, "$Default %cBanks", type_name[0]);
|
||||
sprintf(Bank_Capacity_name, "$%cBank Capacity", type_name[0]);
|
||||
sprintf(Show_Models_name, "$Show %s Models", type_name);
|
||||
|
||||
for(bank = bank_offset; bank < bank_offset + num_banks; bank++)
|
||||
{
|
||||
json_t *regular_bank = json_array();
|
||||
json_t *dogfight_bank = json_array();
|
||||
for(weapon_idx = 0; weapon_idx < Num_weapon_types; weapon_idx++)
|
||||
{
|
||||
restricted = check_restricted(sip, regular_bank, restricted, bank, weapon_idx, REGULAR_WEAPON);
|
||||
dogfight_restricted = check_restricted(sip, dogfight_bank, dogfight_restricted, bank, weapon_idx, DOGFIGHT_WEAPON);
|
||||
}
|
||||
json_array_append(Allowed_banks, regular_bank);
|
||||
json_array_append(Allowed_dogfight_banks, dogfight_bank);
|
||||
|
||||
if(is_primary)
|
||||
{
|
||||
json_array_append(Default_banks, json_string(Weapon_info[sip->primary_bank_weapons[bank - bank_offset]].name));
|
||||
json_array_append(Bank_capacity, json_integer(sip->primary_bank_ammo_capacity[bank - bank_offset]));
|
||||
json_array_append(Show_models, json_boolean(sip->draw_primary_models[bank - bank_offset]));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_array_append(Default_banks, json_string(Weapon_info[sip->secondary_bank_weapons[bank - bank_offset]].name));
|
||||
json_array_append(Bank_capacity, json_integer(sip->secondary_bank_ammo_capacity[bank - bank_offset]));
|
||||
json_array_append(Show_models, json_boolean(sip->draw_secondary_models[bank - bank_offset]));
|
||||
}
|
||||
}
|
||||
json_object_set_new(ship_data, Allowed_Banks_name, (restricted ? Allowed_banks : json_array_get(Allowed_banks, 0)));
|
||||
json_object_set_new(ship_data, Allowed_Dogfight_Banks_name, (dogfight_restricted ? Allowed_dogfight_banks : json_array_get(Allowed_dogfight_banks, 0)));
|
||||
json_object_set_new(ship_data, Default_Banks_name, Default_banks);
|
||||
json_object_set_new(ship_data, Bank_Capacity_name, Bank_capacity);
|
||||
json_object_set_new(ship_data, Show_Models_name, Show_models);
|
||||
}
|
||||
|
||||
void ship_output_json(const char *outfile)
|
||||
{
|
||||
mprintf(("Exporting ship data to json.\n"));
|
||||
|
||||
int i;
|
||||
json_t *ships_obj = json_object();
|
||||
json_t *Ships = json_array();
|
||||
ship_info *sip;
|
||||
species_info *species;
|
||||
char full_name[MAX_PATH_LEN];
|
||||
|
||||
json_object_set_new(ships_obj, "default_player_ship", json_string(default_player_ship));
|
||||
|
||||
// Loop over the Ship_info array and add a new entry to ship_info_arr each time
|
||||
for (i = 0; i < Num_ship_classes; i++)
|
||||
{
|
||||
int j;
|
||||
sip = &Ship_info[i];
|
||||
species = &Species_info[sip->species];
|
||||
json_t *ship_data = json_object();
|
||||
json_t *Displays = json_array();
|
||||
json_t *Replacements = json_array();
|
||||
json_t *Cockpit_offset = json_object();
|
||||
json_t *Detail_distance = json_array();
|
||||
json_t *ND = json_array();
|
||||
json_t *Impact = json_object();
|
||||
json_t *Collision_Physics = json_object();
|
||||
json_t *Debris = json_object();
|
||||
json_t *Max_Velocity = json_object();
|
||||
json_t *Rotation_Time = json_object();
|
||||
json_t *Anims = json_array();
|
||||
json_t *Model_point_shield_controls = json_array();
|
||||
size_t insert_index, shield_point;
|
||||
json_t *quadrant;
|
||||
json_t *Shield_color = json_object();
|
||||
|
||||
json_object_set_new(ship_data, "$Name", json_string(sip->name));
|
||||
json_object_set_new(ship_data, "$Alt Name", json_string(sip->alt_name));
|
||||
json_object_set_new(ship_data, "$Short Name", json_string(sip->short_name));
|
||||
json_object_set_new(ship_data, "$Species", json_string(species->species_name));
|
||||
json_object_set_new(ship_data, "+Type", json_string(sip->type_str));
|
||||
json_object_set_new(ship_data, "+Maneuverability", json_string(sip->maneuverability_str));
|
||||
json_object_set_new(ship_data, "+Armor", json_string(sip->armor_str));
|
||||
json_object_set_new(ship_data, "+Manufacturer", json_string(sip->manufacturer_str));
|
||||
json_object_set_new(ship_data, "+Description", json_string(sip->desc));
|
||||
json_object_set_new(ship_data, "+Tech Title", json_string(sip->tech_title));
|
||||
json_object_set_new(ship_data, "+Tech Description", json_string(sip->tech_desc));
|
||||
json_object_set_new(ship_data, "+Length", json_string(sip->ship_length));
|
||||
json_object_set_new(ship_data, "+Gun Mounts", json_string(sip->gun_mounts));
|
||||
json_object_set_new(ship_data, "+Missile Banks", json_string(sip->missile_banks));
|
||||
switch(sip->selection_effect)
|
||||
{
|
||||
case 0:
|
||||
json_object_set_new(ship_data, "$Selection Effect", json_string("off"));
|
||||
break;
|
||||
case 1:
|
||||
json_object_set_new(ship_data, "$Selection Effect", json_string("FS1"));
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
json_object_set_new(ship_data, "$Selection Effect", json_string("FS2"));
|
||||
}
|
||||
json_object_set_new(ship_data, "$Cockpit POF file", json_string(sip->cockpit_pof_file));
|
||||
json_object_set_new(Cockpit_offset, "x", json_real(sip->cockpit_offset.xyz.x));
|
||||
json_object_set_new(Cockpit_offset, "y", json_real(sip->cockpit_offset.xyz.y));
|
||||
json_object_set_new(Cockpit_offset, "z", json_real(sip->cockpit_offset.xyz.z));
|
||||
json_object_set_new(ship_data, "+Cockpit offset", Cockpit_offset);
|
||||
for(SCP_vector<cockpit_display_info>::iterator display = sip->displays.begin(); display != sip->displays.end(); ++display)
|
||||
{
|
||||
json_t *display_data = json_object();
|
||||
json_t *offsets = json_array();
|
||||
json_t *size = json_array();
|
||||
|
||||
json_object_set_new(display_data, "+Texture", json_string(display->filename));
|
||||
json_array_append_new(offsets, json_integer(display->offset[0]));
|
||||
json_array_append_new(offsets, json_integer(display->offset[1]));
|
||||
json_object_set_new(display_data, "+Offsets", offsets);
|
||||
json_array_append_new(size, json_integer(display->size[0]));
|
||||
json_array_append_new(size, json_integer(display->size[1]));
|
||||
json_object_set_new(display_data, "+Size", size);
|
||||
json_object_set_new(display_data, "+Background", json_string(display->bg_filename));
|
||||
json_object_set_new(display_data, "+Foreground", json_string(display->fg_filename));
|
||||
json_object_set_new(display_data, "+Display Name", json_string(display->name));
|
||||
|
||||
json_array_append_new(Displays, display_data);
|
||||
}
|
||||
json_object_set_new(ship_data, "$Cockpit Display", Displays);
|
||||
json_object_set_new(ship_data, "$POF file", json_string(sip->pof_file));
|
||||
for(SCP_vector<texture_replace>::iterator replace = sip->replacement_textures.begin(); replace != sip->replacement_textures.end(); ++replace)
|
||||
{
|
||||
json_t *replace_data = json_object();
|
||||
|
||||
json_object_set_new(replace_data, "+old", json_string(replace->old_texture));
|
||||
json_object_set_new(replace_data, "+new", json_string(replace->new_texture));
|
||||
|
||||
json_array_append_new(Replacements, replace_data);
|
||||
}
|
||||
json_object_set_new(ship_data, "$Texture Replace", Replacements);
|
||||
json_object_set_new(ship_data, "$POF target file", json_string(sip->pof_file_hud));
|
||||
json_object_set_new(ship_data, "$POF target LOD", json_integer(sip->hud_target_lod));
|
||||
for (j = 0; j < sip->num_detail_levels; j++)
|
||||
{
|
||||
json_array_append_new(Detail_distance, json_integer(sip->detail_distance[j]));
|
||||
}
|
||||
json_object_set_new(ship_data, "$Detail distance", Detail_distance);
|
||||
for (j = 0; j < sip->num_nondark_colors; j++)
|
||||
{
|
||||
json_t *nd_values = json_object();
|
||||
|
||||
json_object_set_new(nd_values, "red", json_integer(sip->nondark_colors[j][0]));
|
||||
json_object_set_new(nd_values, "green", json_integer(sip->nondark_colors[j][1]));
|
||||
json_object_set_new(nd_values, "blue", json_integer(sip->nondark_colors[j][2]));
|
||||
|
||||
json_array_append_new(ND, nd_values);
|
||||
}
|
||||
json_object_set_new(ship_data, "$ND", ND);
|
||||
json_object_set_new(ship_data, "$Enable Team Colors", json_boolean(sip->uses_team_colors));
|
||||
json_object_set_new(ship_data, "$Default Team", json_string(sip->default_team_name.c_str()));
|
||||
json_object_set_new(ship_data, "$Damage Lightning Type", json_string(Lightning_types[sip->damage_lightning_type]));
|
||||
if(sip->collision_damage_type_idx > -1 && sip->collision_damage_type_idx < Damage_types.size())
|
||||
{
|
||||
json_object_set_new(Impact, "+Damage Type", json_string(Damage_types.at(sip->collision_damage_type_idx).name));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_set_new(Impact, "+Damage Type", json_null());
|
||||
}
|
||||
json_object_set_new(ship_data, "$Impact", Impact);
|
||||
// Skipping spews
|
||||
json_object_set_new(Collision_Physics, "+Bounce", json_real(sip->collision_physics.bounce));
|
||||
json_object_set_new(Collision_Physics, "+Both Small Bounce", json_real(sip->collision_physics.both_small_bounce));
|
||||
json_object_set_new(Collision_Physics, "+Friction", json_real(sip->collision_physics.friction));
|
||||
json_object_set_new(Collision_Physics, "+Rotation Factor", json_real(sip->collision_physics.rotation_factor));
|
||||
json_object_set_new(Collision_Physics, "+Landing Max Forward Vel", json_real(sip->collision_physics.landing_max_z));
|
||||
json_object_set_new(Collision_Physics, "+Landing Min Forward Vel", json_real(sip->collision_physics.landing_min_z));
|
||||
json_object_set_new(Collision_Physics, "+Landing Max Descent Vel", json_real(sip->collision_physics.landing_min_y));
|
||||
json_object_set_new(Collision_Physics, "+Landing Max Horizontal Vel", json_real(sip->collision_physics.landing_max_x));
|
||||
json_object_set_new(Collision_Physics, "+Landing Max Angle", json_real(sip->collision_physics.landing_max_angle));
|
||||
json_object_set_new(Collision_Physics, "+Landing Min Angle", json_real(sip->collision_physics.landing_min_angle));
|
||||
json_object_set_new(Collision_Physics, "+Landing Max Rotate Angle", json_real(sip->collision_physics.landing_max_rot_angle));
|
||||
json_object_set_new(Collision_Physics, "+Reorient Max Forward Vel", json_real(sip->collision_physics.reorient_max_z));
|
||||
json_object_set_new(Collision_Physics, "+Reorient Min Forward Vel", json_real(sip->collision_physics.reorient_min_z));
|
||||
json_object_set_new(Collision_Physics, "+Reorient Max Descent Vel", json_real(sip->collision_physics.reorient_min_y));
|
||||
json_object_set_new(Collision_Physics, "+Reorient Max Horizontal Vel", json_real(sip->collision_physics.reorient_max_x));
|
||||
json_object_set_new(Collision_Physics, "+Reorient Max Angle", json_real(sip->collision_physics.reorient_max_angle));
|
||||
json_object_set_new(Collision_Physics, "+Reorient Min Angle", json_real(sip->collision_physics.reorient_min_angle));
|
||||
json_object_set_new(Collision_Physics, "+Reorient Max Rotate Angle", json_real(sip->collision_physics.reorient_max_rot_angle));
|
||||
json_object_set_new(Collision_Physics, "+Reorient Speed Mult", json_real(sip->collision_physics.reorient_mult));
|
||||
json_object_set_new(Collision_Physics, "+Landing Rest Angle", json_real(sip->collision_physics.landing_rest_angle));
|
||||
if(sip->collision_damage_type_idx > -1 && sip->collision_damage_type_idx < Snds.size())
|
||||
{
|
||||
json_object_set_new(Collision_Physics, "+Landing Sound", json_string(Snds.at(sip->collision_physics.landing_sound_idx).name.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_set_new(Collision_Physics, "+Landing Sound", json_null());
|
||||
}
|
||||
json_object_set_new(ship_data, "$Collision Physics", Collision_Physics);
|
||||
json_object_set_new(Debris, "+Min Lifetime", json_real(sip->debris_min_lifetime));
|
||||
json_object_set_new(Debris, "+Max Lifetime", json_real(sip->debris_max_lifetime));
|
||||
json_object_set_new(Debris, "+Min Speed", json_real(sip->debris_min_speed));
|
||||
json_object_set_new(Debris, "+Max Speed", json_real(sip->debris_max_speed));
|
||||
json_object_set_new(Debris, "+Min Rotation speed", json_real(sip->debris_min_rotspeed));
|
||||
json_object_set_new(Debris, "+Max Rotation speed", json_real(sip->debris_max_rotspeed));
|
||||
if(sip->collision_damage_type_idx > -1 && sip->collision_damage_type_idx < Damage_types.size())
|
||||
{
|
||||
json_object_set_new(Debris, "+Damage Type", json_string(Damage_types.at(sip->debris_damage_type_idx).name));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_set_new(Debris, "+Damage Type", json_null());
|
||||
}
|
||||
json_object_set_new(Debris, "+Min Hitpoints", json_real(sip->debris_min_hitpoints));
|
||||
json_object_set_new(Debris, "+Max Hitpoints", json_real(sip->debris_max_hitpoints));
|
||||
json_object_set_new(Debris, "+Damage Multiplier", json_real(sip->debris_damage_mult));
|
||||
json_object_set_new(Debris, "+Lightning Arc Percent", json_real(sip->debris_arc_percent * 100.0f));
|
||||
json_object_set_new(ship_data, "$Debris", Debris);
|
||||
json_object_set_new(ship_data, "$Density", json_real(sip->density));
|
||||
json_object_set_new(ship_data, "$Damp", json_real(sip->damp));
|
||||
json_object_set_new(ship_data, "$Rotdamp", json_real(sip->rotdamp));
|
||||
json_object_set_new(ship_data, "$Banking Constant", json_real(sip->delta_bank_const));
|
||||
json_object_set_new(Max_Velocity, "x", json_real(sip->max_vel.xyz.x));
|
||||
json_object_set_new(Max_Velocity, "y", json_real(sip->max_vel.xyz.y));
|
||||
json_object_set_new(Max_Velocity, "z", json_real(sip->max_vel.xyz.z));
|
||||
json_object_set_new(ship_data, "$Max Velocity", Max_Velocity);
|
||||
json_object_set_new(Rotation_Time, "x", json_real(sip->rotation_time.xyz.x));
|
||||
json_object_set_new(Rotation_Time, "y", json_real(sip->rotation_time.xyz.y));
|
||||
json_object_set_new(Rotation_Time, "z", json_real(sip->rotation_time.xyz.z));
|
||||
json_object_set_new(ship_data, "$Rotation Time", Rotation_Time);
|
||||
json_object_set_new(ship_data, "$Rear Velocity", json_real(sip->max_rear_vel));
|
||||
json_object_set_new(ship_data, "$Forward accel", json_real(sip->forward_accel));
|
||||
json_object_set_new(ship_data, "$Forward decel", json_real(sip->forward_decel));
|
||||
json_object_set_new(ship_data, "$Slide accel", json_real(sip->slide_accel));
|
||||
json_object_set_new(ship_data, "$Slide decel", json_real(sip->slide_decel));
|
||||
json_object_set_new(ship_data, "$Glide", json_boolean(sip->can_glide));
|
||||
json_object_set_new(ship_data, "+Dynamic Glide Cap", json_boolean(sip->glide_dynamic_cap));
|
||||
json_object_set_new(ship_data, "+Max Glide Speed", json_real(sip->glide_cap));
|
||||
json_object_set_new(ship_data, "+Glide Accel Mult", json_real(sip->glide_accel_mult));
|
||||
json_object_set_new(ship_data, "$Use Newtonian Dampening", json_boolean(sip->use_newtonian_damp));
|
||||
json_object_set_new(ship_data, "$Autoaim FOV", json_integer(sip->autoaim_fov));
|
||||
// Skipping convergence, ugh...
|
||||
json_object_set_new(ship_data, "$Warpin type", json_string(Warp_types[sip->warpin_type]));
|
||||
if(sip->warpin_snd_start > -1 && sip->warpin_snd_start < Snds.size())
|
||||
{
|
||||
json_object_set_new(ship_data, "$Warpin Start Sound", json_string(Snds.at(sip->warpin_snd_start).name.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_set_new(ship_data, "$Warpin Start Sound", json_null());
|
||||
}
|
||||
if(sip->warpin_snd_end > -1 && sip->warpin_snd_end < Snds.size())
|
||||
{
|
||||
json_object_set_new(ship_data, "$Warpin End Sound", json_string(Snds.at(sip->warpin_snd_end).name.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_set_new(ship_data, "$Warpin End Sound", json_null());
|
||||
}
|
||||
json_object_set_new(ship_data, "$Warpin speed", json_real(sip->warpin_speed));
|
||||
json_object_set_new(ship_data, "$Warpin time", json_real(sip->warpin_time));
|
||||
json_object_set_new(ship_data, "$Warpin decel exp", json_real(sip->warpin_decel_exp));
|
||||
json_object_set_new(ship_data, "$Warpin radius", json_real(sip->warpin_radius));
|
||||
json_object_set_new(ship_data, "$Warpin animation", json_string(sip->warpin_anim));
|
||||
json_object_set_new(ship_data, "$Warpout type", json_string(Warp_types[sip->warpout_type]));
|
||||
if(sip->warpout_snd_start > -1 && sip->warpout_snd_start < Snds.size())
|
||||
{
|
||||
json_object_set_new(ship_data, "$Warpout Start Sound", json_string(Snds.at(sip->warpout_snd_start).name.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_set_new(ship_data, "$Warpout Start Sound", json_null());
|
||||
}
|
||||
if(sip->warpout_snd_end > -1 && sip->warpout_snd_end < Snds.size())
|
||||
{
|
||||
json_object_set_new(ship_data, "$Warpout End Sound", json_string(Snds.at(sip->warpout_snd_end).name.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_set_new(ship_data, "$Warpout End Sound", json_null());
|
||||
}
|
||||
json_object_set_new(ship_data, "$Warpout engage time", json_real(sip->warpout_engage_time));
|
||||
json_object_set_new(ship_data, "$Warpout speed", json_real(sip->warpout_speed));
|
||||
json_object_set_new(ship_data, "$Warpout time", json_real(sip->warpout_time));
|
||||
json_object_set_new(ship_data, "$Warpout accel exp", json_real(sip->warpout_accel_exp));
|
||||
json_object_set_new(ship_data, "$Warpout radius", json_real(sip->warpout_radius));
|
||||
json_object_set_new(ship_data, "$Warpout animation", json_string(sip->warpout_anim));
|
||||
json_object_set_new(ship_data, "$Player warpout speed", json_real(sip->warpout_player_speed));
|
||||
json_object_set_new(ship_data, "$Expl inner rad", json_real(sip->shockwave.inner_rad));
|
||||
json_object_set_new(ship_data, "$Expl outer rad", json_real(sip->shockwave.outer_rad));
|
||||
json_object_set_new(ship_data, "$Expl damage", json_real(sip->shockwave.damage));
|
||||
json_object_set_new(ship_data, "$Expl blast", json_real(sip->shockwave.blast));
|
||||
json_object_set_new(ship_data, "$Expl Propogates", json_boolean(sip->explosion_propagates));
|
||||
json_object_set_new(ship_data, "$Propagating Expl Radius Multiplier", json_real(sip->prop_exp_rad_mult));
|
||||
json_object_set_new(ship_data, "$Expl Visual Rad", json_real(sip->big_exp_visual_rad));
|
||||
json_object_set_new(ship_data, "$Base Death-Roll Time", json_integer(sip->death_roll_base_time));
|
||||
json_object_set_new(ship_data, "$Death-Roll Explosion Radius Mult", json_real(sip->death_roll_r_mult));
|
||||
json_object_set_new(ship_data, "$Death-Roll Explosion Intensity Mult", json_real(sip->death_roll_time_mult));
|
||||
json_object_set_new(ship_data, "$Death FX Explosion Radius Mult", json_real(sip->death_fx_r_mult));
|
||||
json_object_set_new(ship_data, "$Death FX Explosion Count", json_integer(sip->death_fx_count));
|
||||
// Skipping more particle stuff for now
|
||||
json_object_set_new(ship_data, "$Vaporize Percent Chance", json_real(sip->vaporize_chance));
|
||||
if(sip->shockwave.damage_type_idx > -1 && sip->shockwave.damage_type_idx < Damage_types.size())
|
||||
{
|
||||
json_object_set_new(ship_data, "$Shockwave Damage Type", json_string(Damage_types.at(sip->shockwave.damage_type_idx).name));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_object_set_new(ship_data, "$Shockwave Damage Type", json_null());
|
||||
}
|
||||
json_object_set_new(ship_data, "$Shockwave Speed", json_real(sip->shockwave.speed));
|
||||
json_object_set_new(ship_data, "$Shockwave Count", json_real(sip->shockwave_count));
|
||||
json_object_set_new(ship_data, "$Shockwave model", json_string(sip->shockwave.pof_name));
|
||||
json_object_set_new(ship_data, "$Shockwave name", json_string(sip->shockwave.name));
|
||||
for(SCP_vector<int>::iterator anim = sip->explosion_bitmap_anims.begin(); anim != sip->explosion_bitmap_anims.end(); ++anim)
|
||||
{
|
||||
json_array_append_new(Anims, json_integer(*anim));
|
||||
}
|
||||
json_object_set_new(ship_data, "$Explosion Animations", Anims);
|
||||
json_object_set_new(ship_data, "$Weapon Model Draw Distance", json_real(sip->weapon_model_draw_distance));
|
||||
append_weapons(sip, ship_data, true, 0);
|
||||
append_weapons(sip, ship_data, false, MAX_SHIP_PRIMARY_BANKS);
|
||||
json_object_set_new(ship_data, "$Shields", json_real(sip->max_shield_strength));
|
||||
json_object_set_new(ship_data, "+Auto Spread", json_real(sip->auto_shield_spread));
|
||||
json_object_set_new(ship_data, "+Allow Bypass", json_boolean(sip->auto_shield_spread_bypass));
|
||||
json_object_set_new(ship_data, "+Spread From LOD", json_integer(sip->auto_shield_spread_from_lod));
|
||||
for(j = 0; j < sizeof(sip->shield_point_augment_ctrls) / sizeof(int); j++)
|
||||
{
|
||||
if(sip->shield_point_augment_ctrls[j] != -1)
|
||||
{
|
||||
insert_index = 0;
|
||||
json_array_foreach(Model_point_shield_controls, shield_point, quadrant)
|
||||
{
|
||||
insert_index = shield_point;
|
||||
if(sip->shield_point_augment_ctrls[j] < shield_point)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch(j)
|
||||
{
|
||||
case 0:
|
||||
json_array_insert_new(Model_point_shield_controls, insert_index, json_string("right"));
|
||||
break;
|
||||
case 1:
|
||||
json_array_insert_new(Model_point_shield_controls, insert_index, json_string("front"));
|
||||
break;
|
||||
case 2:
|
||||
json_array_insert_new(Model_point_shield_controls, insert_index, json_string("rear"));
|
||||
break;
|
||||
case 3:
|
||||
json_array_insert_new(Model_point_shield_controls, insert_index, json_string("left"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
json_object_set_new(ship_data, "$Model Point Shield Controls", Model_point_shield_controls);
|
||||
json_object_set_new(Shield_color, "red", json_integer(sip->shield_color[0]));
|
||||
json_object_set_new(Shield_color, "green", json_integer(sip->shield_color[1]));
|
||||
json_object_set_new(Shield_color, "blue", json_integer(sip->shield_color[2]));
|
||||
json_object_set_new(ship_data, "$Shield Color", Shield_color);
|
||||
json_object_set_new(ship_data, "$Power Output", json_real(sip->power_output));
|
||||
json_object_set_new(ship_data, "$Shield Regeneration Rate", json_real(sip->max_shield_regen_per_second));
|
||||
json_object_set_new(ship_data, "$Support Shield Repair Rate", json_real(sip->sup_shield_repair_rate * 100));
|
||||
json_object_set_new(ship_data, "$Weapon Regeneration Rate", json_real(sip->max_weapon_regen_per_second));
|
||||
json_object_set_new(ship_data, "$Max Oclk Speed", json_real(sip->max_overclocked_speed));
|
||||
json_object_set_new(ship_data, "$Max Weapon Eng", json_real(sip->max_weapon_reserve));
|
||||
json_object_set_new(ship_data, "$Hitpoints", json_real(sip->max_hull_strength));
|
||||
json_object_set_new(ship_data, "$Hull Repair Rate", json_real(sip->hull_repair_rate * 100));
|
||||
json_object_set_new(ship_data, "$Support Hull Repair Rate", json_real(sip->sup_hull_repair_rate * 100));
|
||||
json_object_set_new(ship_data, "$Subsystem Repair Rate", json_real(sip->subsys_repair_rate * 100));
|
||||
json_object_set_new(ship_data, "$Support Subsystem Repair Rate", json_real(sip->sup_subsys_repair_rate * 100));
|
||||
json_object_set_new(ship_data, "$Armor Type", ( (sip->armor_type_idx > -1) ? json_string(Armor_types[sip->armor_type_idx].GetNamePtr()) : json_null() ));
|
||||
json_object_set_new(ship_data, "$Shield Armor Type", ( (sip->shield_armor_type_idx > -1) ? json_string(Armor_types[sip->shield_armor_type_idx].GetNamePtr()) : json_null() ));
|
||||
// Skipping flags
|
||||
|
||||
json_array_append_new(Ships, ship_data);
|
||||
}
|
||||
|
||||
json_object_set_new(ships_obj, "Ships", Ships);
|
||||
|
||||
cf_create_default_path_string(full_name, sizeof(full_name) - 1, CF_TYPE_ROOT, outfile);
|
||||
json_dump_file(ships_obj, full_name, JSON_ESCAPE_SLASH | JSON_INDENT(4) | JSON_PRESERVE_ORDER | JSON_REAL_PRECISION(4));
|
||||
}
|
||||
@@ -2006,4 +2006,6 @@ int get_default_player_ship_index();
|
||||
*/
|
||||
int get_nearest_bbox_point(object *ship_obj, vec3d *start, vec3d *box_pt);
|
||||
|
||||
extern void ship_output_json(const char *outfile);
|
||||
|
||||
#endif
|
||||
@@ -670,4 +670,7 @@ void weapon_pause_sounds();
|
||||
// Unpauses all running weapon sounds
|
||||
void weapon_unpause_sounds();
|
||||
|
||||
// Dumps parsed weapon data to a json file
|
||||
extern void weapons_output_json(const char *outfile);
|
||||
|
||||
#endif
|
||||
@@ -7022,3 +7022,8 @@ void weapon_unpause_sounds()
|
||||
// Pause all beam sounds
|
||||
beam_unpause_sounds();
|
||||
}
|
||||
|
||||
void weapons_output_json(const char *outfile)
|
||||
{
|
||||
mprintf(("Exporting weapon data to json.\n"));
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2013 Petri Lehtinen <[email protected]>
|
||||
Copyright (c) 2009-2014 Petri Lehtinen <[email protected]>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+19
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <[email protected]>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <[email protected]>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -22,6 +22,9 @@
|
||||
#define MAX_INTEGER_STR_LENGTH 100
|
||||
#define MAX_REAL_STR_LENGTH 100
|
||||
|
||||
#define FLAGS_TO_INDENT(f) ((f) & 0x1F)
|
||||
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
|
||||
|
||||
struct object_key {
|
||||
size_t serial;
|
||||
const char *key;
|
||||
@@ -45,9 +48,9 @@ static const char whitespace[] = " ";
|
||||
|
||||
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
|
||||
{
|
||||
if(JSON_INDENT(flags) > 0)
|
||||
if(FLAGS_TO_INDENT(flags) > 0)
|
||||
{
|
||||
int i, ws_count = JSON_INDENT(flags);
|
||||
int i, ws_count = FLAGS_TO_INDENT(flags);
|
||||
|
||||
if(dump("\n", 1, data))
|
||||
return -1;
|
||||
@@ -65,24 +68,25 @@ static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags)
|
||||
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
|
||||
{
|
||||
const char *pos, *end;
|
||||
const char *pos, *end, *lim;
|
||||
int32_t codepoint;
|
||||
|
||||
if(dump("\"", 1, data))
|
||||
return -1;
|
||||
|
||||
end = pos = str;
|
||||
lim = str + len;
|
||||
while(1)
|
||||
{
|
||||
const char *text;
|
||||
char seq[13];
|
||||
int length;
|
||||
|
||||
while(*end)
|
||||
while(end < lim)
|
||||
{
|
||||
end = utf8_iterate(pos, &codepoint);
|
||||
end = utf8_iterate(pos, lim - pos, &codepoint);
|
||||
if(!end)
|
||||
return -1;
|
||||
|
||||
@@ -126,7 +130,7 @@ static int dump_string(const char *str, json_dump_callback_t dump, void *data, s
|
||||
/* codepoint is in BMP */
|
||||
if(codepoint < 0x10000)
|
||||
{
|
||||
sprintf(seq, "\\u%04x", codepoint);
|
||||
sprintf(seq, "\\u%04X", codepoint);
|
||||
length = 6;
|
||||
}
|
||||
|
||||
@@ -139,7 +143,7 @@ static int dump_string(const char *str, json_dump_callback_t dump, void *data, s
|
||||
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
|
||||
last = 0xDC00 | (codepoint & 0x003ff);
|
||||
|
||||
sprintf(seq, "\\u%04x\\u%04x", first, last);
|
||||
sprintf(seq, "\\u%04X\\u%04X", first, last);
|
||||
length = 12;
|
||||
}
|
||||
|
||||
@@ -207,7 +211,8 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
int size;
|
||||
double value = json_real_value(json);
|
||||
|
||||
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
|
||||
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
|
||||
FLAGS_TO_PRECISION(flags));
|
||||
if(size < 0)
|
||||
return -1;
|
||||
|
||||
@@ -215,7 +220,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
}
|
||||
|
||||
case JSON_STRING:
|
||||
return dump_string(json_string_value(json), dump, data, flags);
|
||||
return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
|
||||
|
||||
case JSON_ARRAY:
|
||||
{
|
||||
@@ -336,7 +341,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
value = json_object_get(json, key);
|
||||
assert(value);
|
||||
|
||||
dump_string(key, dump, data, flags);
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(value, flags, depth + 1, dump, data))
|
||||
{
|
||||
@@ -372,8 +377,9 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
while(iter)
|
||||
{
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
const char *key = json_object_iter_key(iter);
|
||||
|
||||
dump_string(json_object_iter_key(iter), dump, data, flags);
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
|
||||
+36
-44
@@ -1,12 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <[email protected]>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <[email protected]>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <jansson_config.h> /* for JSON_INLINE */
|
||||
#include "jansson_private.h" /* for container_of() */
|
||||
#include "hashtable.h"
|
||||
@@ -15,24 +24,13 @@ typedef struct hashtable_list list_t;
|
||||
typedef struct hashtable_pair pair_t;
|
||||
typedef struct hashtable_bucket bucket_t;
|
||||
|
||||
extern volatile uint32_t hashtable_seed;
|
||||
|
||||
/* Implementation of the hash function */
|
||||
#include "lookup3.h"
|
||||
|
||||
#define list_to_pair(list_) container_of(list_, pair_t, list)
|
||||
|
||||
/* From http://www.cse.yorku.ca/~oz/hash.html */
|
||||
static size_t hash_str(const void *ptr)
|
||||
{
|
||||
const char *str = (const char *)ptr;
|
||||
|
||||
size_t hash = 5381;
|
||||
size_t c;
|
||||
|
||||
while((c = (size_t)*str))
|
||||
{
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
str++;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
|
||||
|
||||
static JSON_INLINE void list_init(list_t *list)
|
||||
{
|
||||
@@ -74,19 +72,6 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
|
||||
}
|
||||
}
|
||||
|
||||
static const size_t primes[] = {
|
||||
5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
|
||||
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
|
||||
12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
|
||||
805306457, 1610612741
|
||||
};
|
||||
|
||||
static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
|
||||
{
|
||||
return primes[hashtable->num_buckets];
|
||||
}
|
||||
|
||||
|
||||
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
const char *key, size_t hash)
|
||||
{
|
||||
@@ -120,7 +105,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
||||
bucket_t *bucket;
|
||||
size_t index;
|
||||
|
||||
index = hash % num_buckets(hashtable);
|
||||
index = hash & hashmask(hashtable->order);
|
||||
bucket = &hashtable->buckets[index];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
@@ -167,14 +152,14 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||
|
||||
jsonp_free(hashtable->buckets);
|
||||
|
||||
hashtable->num_buckets++;
|
||||
new_size = num_buckets(hashtable);
|
||||
hashtable->order++;
|
||||
new_size = hashsize(hashtable->order);
|
||||
|
||||
hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
@@ -199,14 +184,14 @@ int hashtable_init(hashtable_t *hashtable)
|
||||
size_t i;
|
||||
|
||||
hashtable->size = 0;
|
||||
hashtable->num_buckets = 0; /* index to primes[] */
|
||||
hashtable->buckets = jsonp_malloc(num_buckets(hashtable) * sizeof(bucket_t));
|
||||
hashtable->order = 3;
|
||||
hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
|
||||
list_init(&hashtable->list);
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
@@ -230,12 +215,12 @@ int hashtable_set(hashtable_t *hashtable,
|
||||
size_t hash, index;
|
||||
|
||||
/* rehash if the load ratio exceeds 1 */
|
||||
if(hashtable->size >= num_buckets(hashtable))
|
||||
if(hashtable->size >= hashsize(hashtable->order))
|
||||
if(hashtable_do_rehash(hashtable))
|
||||
return -1;
|
||||
|
||||
hash = hash_str(key);
|
||||
index = hash % num_buckets(hashtable);
|
||||
index = hash & hashmask(hashtable->order);
|
||||
bucket = &hashtable->buckets[index];
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
|
||||
@@ -249,7 +234,14 @@ int hashtable_set(hashtable_t *hashtable,
|
||||
/* offsetof(...) returns the size of pair_t without the last,
|
||||
flexible member. This way, the correct amount is
|
||||
allocated. */
|
||||
pair = jsonp_malloc(offsetof(pair_t, key) + strlen(key) + 1);
|
||||
|
||||
size_t len = strlen(key);
|
||||
if(len >= (size_t)-1 - offsetof(pair_t, key)) {
|
||||
/* Avoid an overflow if the key is very long */
|
||||
return -1;
|
||||
}
|
||||
|
||||
pair = jsonp_malloc(offsetof(pair_t, key) + len + 1);
|
||||
if(!pair)
|
||||
return -1;
|
||||
|
||||
@@ -273,7 +265,7 @@ void *hashtable_get(hashtable_t *hashtable, const char *key)
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hash_str(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
@@ -294,7 +286,7 @@ void hashtable_clear(hashtable_t *hashtable)
|
||||
|
||||
hashtable_do_clear(hashtable);
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
@@ -316,7 +308,7 @@ void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hash_str(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <[email protected]>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <[email protected]>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -32,7 +32,7 @@ struct hashtable_bucket {
|
||||
typedef struct hashtable {
|
||||
size_t size;
|
||||
struct hashtable_bucket *buckets;
|
||||
size_t num_buckets; /* index to primes[] */
|
||||
size_t order; /* hashtable has pow(2, order) buckets */
|
||||
struct hashtable_list list;
|
||||
} hashtable_t;
|
||||
|
||||
@@ -40,6 +40,7 @@ typedef struct hashtable {
|
||||
#define hashtable_key_to_iter(key_) \
|
||||
(&(container_of(key_, struct hashtable_pair, key)->list))
|
||||
|
||||
|
||||
/**
|
||||
* hashtable_init - Initialize a hashtable object
|
||||
*
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
/* Generate sizeof(uint32_t) bytes of as random data as possible to seed
|
||||
the hash function.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SCHED_H
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "jansson.h"
|
||||
|
||||
|
||||
static uint32_t buf_to_uint32(char *data) {
|
||||
size_t i;
|
||||
uint32_t result = 0;
|
||||
|
||||
for (i = 0; i < sizeof(uint32_t); i++)
|
||||
result = (result << 8) | (unsigned char)data[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* /dev/urandom */
|
||||
#if !defined(_WIN32) && defined(USE_URANDOM)
|
||||
static int seed_from_urandom(uint32_t *seed) {
|
||||
/* Use unbuffered I/O if we have open(), close() and read(). Otherwise
|
||||
fall back to fopen() */
|
||||
|
||||
char data[sizeof(uint32_t)];
|
||||
int ok;
|
||||
|
||||
#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ)
|
||||
int urandom;
|
||||
urandom = open("/dev/urandom", O_RDONLY);
|
||||
if (urandom == -1)
|
||||
return 1;
|
||||
|
||||
ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t);
|
||||
close(urandom);
|
||||
#else
|
||||
FILE *urandom;
|
||||
|
||||
urandom = fopen("/dev/urandom", "rb");
|
||||
if (!urandom)
|
||||
return 1;
|
||||
|
||||
ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t);
|
||||
fclose(urandom);
|
||||
#endif
|
||||
|
||||
if (!ok)
|
||||
return 1;
|
||||
|
||||
*seed = buf_to_uint32(data);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Windows Crypto API */
|
||||
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
|
||||
#include <wincrypt.h>
|
||||
|
||||
typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags);
|
||||
typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
|
||||
typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags);
|
||||
|
||||
static int seed_from_windows_cryptoapi(uint32_t *seed)
|
||||
{
|
||||
HINSTANCE hAdvAPI32 = NULL;
|
||||
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
|
||||
CRYPTGENRANDOM pCryptGenRandom = NULL;
|
||||
CRYPTRELEASECONTEXT pCryptReleaseContext = NULL;
|
||||
HCRYPTPROV hCryptProv = 0;
|
||||
BYTE data[sizeof(uint32_t)];
|
||||
int ok;
|
||||
|
||||
hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll"));
|
||||
if(hAdvAPI32 == NULL)
|
||||
return 1;
|
||||
|
||||
pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA");
|
||||
if (!pCryptAcquireContext)
|
||||
return 1;
|
||||
|
||||
pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom");
|
||||
if (!pCryptGenRandom)
|
||||
return 1;
|
||||
|
||||
pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext");
|
||||
if (!pCryptReleaseContext)
|
||||
return 1;
|
||||
|
||||
if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
||||
return 1;
|
||||
|
||||
ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data);
|
||||
pCryptReleaseContext(hCryptProv, 0);
|
||||
|
||||
if (!ok)
|
||||
return 1;
|
||||
|
||||
*seed = buf_to_uint32((char *)data);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* gettimeofday() and getpid() */
|
||||
static int seed_from_timestamp_and_pid(uint32_t *seed) {
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
/* XOR of seconds and microseconds */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
*seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec;
|
||||
#else
|
||||
/* Seconds only */
|
||||
*seed = (uint32_t)time(NULL);
|
||||
#endif
|
||||
|
||||
/* XOR with PID for more randomness */
|
||||
#if defined(_WIN32)
|
||||
*seed ^= (uint32_t)GetCurrentProcessId();
|
||||
#elif defined(HAVE_GETPID)
|
||||
*seed ^= (uint32_t)getpid();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t generate_seed() {
|
||||
uint32_t seed;
|
||||
int done = 0;
|
||||
|
||||
#if !defined(_WIN32) && defined(USE_URANDOM)
|
||||
if (!done && seed_from_urandom(&seed) == 0)
|
||||
done = 1;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
|
||||
if (!done && seed_from_windows_cryptoapi(&seed) == 0)
|
||||
done = 1;
|
||||
#endif
|
||||
|
||||
if (!done) {
|
||||
/* Fall back to timestamp and PID if no better randomness is
|
||||
available */
|
||||
seed_from_timestamp_and_pid(&seed);
|
||||
}
|
||||
|
||||
/* Make sure the seed is never zero */
|
||||
if (seed == 0)
|
||||
seed = 1;
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
|
||||
volatile uint32_t hashtable_seed = 0;
|
||||
|
||||
#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
|
||||
static volatile char seed_initialized = 0;
|
||||
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) {
|
||||
/* Do the seeding ourselves */
|
||||
if (new_seed == 0)
|
||||
new_seed = generate_seed();
|
||||
|
||||
__atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE);
|
||||
} else {
|
||||
/* Wait for another thread to do the seeding */
|
||||
do {
|
||||
#ifdef HAVE_SCHED_YIELD
|
||||
sched_yield();
|
||||
#endif
|
||||
} while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (new_seed == 0) {
|
||||
/* Explicit synchronization fences are not supported by the
|
||||
__sync builtins, so every thread getting here has to
|
||||
generate the seed value.
|
||||
*/
|
||||
new_seed = generate_seed();
|
||||
}
|
||||
|
||||
do {
|
||||
if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) {
|
||||
/* We were the first to seed */
|
||||
break;
|
||||
} else {
|
||||
/* Wait for another thread to do the seeding */
|
||||
#ifdef HAVE_SCHED_YIELD
|
||||
sched_yield();
|
||||
#endif
|
||||
}
|
||||
} while(hashtable_seed == 0);
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
static long seed_initialized = 0;
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (InterlockedIncrement(&seed_initialized) == 1) {
|
||||
/* Do the seeding ourselves */
|
||||
if (new_seed == 0)
|
||||
new_seed = generate_seed();
|
||||
|
||||
hashtable_seed = new_seed;
|
||||
} else {
|
||||
/* Wait for another thread to do the seeding */
|
||||
do {
|
||||
SwitchToThread();
|
||||
} while (hashtable_seed == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Fall back to a thread-unsafe version */
|
||||
void json_object_seed(size_t seed) {
|
||||
uint32_t new_seed = (uint32_t)seed;
|
||||
|
||||
if (hashtable_seed == 0) {
|
||||
if (new_seed == 0)
|
||||
new_seed = generate_seed();
|
||||
|
||||
hashtable_seed = new_seed;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+28
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <[email protected]>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <[email protected]>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -21,11 +21,11 @@ extern "C" {
|
||||
/* version */
|
||||
|
||||
#define JANSSON_MAJOR_VERSION 2
|
||||
#define JANSSON_MINOR_VERSION 5
|
||||
#define JANSSON_MINOR_VERSION 7
|
||||
#define JANSSON_MICRO_VERSION 0
|
||||
|
||||
/* Micro version is omitted if it's 0 */
|
||||
#define JANSSON_VERSION "2.5"
|
||||
#define JANSSON_VERSION "2.7"
|
||||
|
||||
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
|
||||
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
|
||||
@@ -67,23 +67,26 @@ typedef long json_int_t;
|
||||
#endif
|
||||
|
||||
#define json_typeof(json) ((json)->type)
|
||||
#define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT)
|
||||
#define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY)
|
||||
#define json_is_string(json) (json && json_typeof(json) == JSON_STRING)
|
||||
#define json_is_integer(json) (json && json_typeof(json) == JSON_INTEGER)
|
||||
#define json_is_real(json) (json && json_typeof(json) == JSON_REAL)
|
||||
#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT)
|
||||
#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY)
|
||||
#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING)
|
||||
#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER)
|
||||
#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL)
|
||||
#define json_is_number(json) (json_is_integer(json) || json_is_real(json))
|
||||
#define json_is_true(json) (json && json_typeof(json) == JSON_TRUE)
|
||||
#define json_is_false(json) (json && json_typeof(json) == JSON_FALSE)
|
||||
#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE)
|
||||
#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE)
|
||||
#define json_boolean_value json_is_true
|
||||
#define json_is_boolean(json) (json_is_true(json) || json_is_false(json))
|
||||
#define json_is_null(json) (json && json_typeof(json) == JSON_NULL)
|
||||
#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
|
||||
|
||||
/* construction, destruction, reference counting */
|
||||
|
||||
json_t *json_object(void);
|
||||
json_t *json_array(void);
|
||||
json_t *json_string(const char *value);
|
||||
json_t *json_stringn(const char *value, size_t len);
|
||||
json_t *json_string_nocheck(const char *value);
|
||||
json_t *json_stringn_nocheck(const char *value, size_t len);
|
||||
json_t *json_integer(json_int_t value);
|
||||
json_t *json_real(double value);
|
||||
json_t *json_true(void);
|
||||
@@ -126,6 +129,7 @@ typedef struct {
|
||||
|
||||
/* getters, setters, manipulation */
|
||||
|
||||
void json_object_seed(size_t seed);
|
||||
size_t json_object_size(const json_t *object);
|
||||
json_t *json_object_get(const json_t *object, const char *key);
|
||||
int json_object_set_new(json_t *object, const char *key, json_t *value);
|
||||
@@ -199,16 +203,18 @@ int json_array_insert(json_t *array, size_t ind, json_t *value)
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *string);
|
||||
size_t json_string_length(const json_t *string);
|
||||
json_int_t json_integer_value(const json_t *integer);
|
||||
double json_real_value(const json_t *real);
|
||||
double json_number_value(const json_t *json);
|
||||
|
||||
int json_string_set(json_t *string, const char *value);
|
||||
int json_string_setn(json_t *string, const char *value, size_t len);
|
||||
int json_string_set_nocheck(json_t *string, const char *value);
|
||||
int json_string_setn_nocheck(json_t *string, const char *value, size_t len);
|
||||
int json_integer_set(json_t *integer, json_int_t value);
|
||||
int json_real_set(json_t *real, double value);
|
||||
|
||||
|
||||
/* pack, unpack */
|
||||
|
||||
json_t *json_pack(const char *fmt, ...);
|
||||
@@ -240,6 +246,7 @@ json_t *json_deep_copy(const json_t *value);
|
||||
#define JSON_DISABLE_EOF_CHECK 0x2
|
||||
#define JSON_DECODE_ANY 0x4
|
||||
#define JSON_DECODE_INT_AS_REAL 0x8
|
||||
#define JSON_ALLOW_NUL 0x10
|
||||
|
||||
typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
|
||||
|
||||
@@ -252,13 +259,15 @@ json_t *json_load_callback(json_load_callback_t callback, void *data, size_t fla
|
||||
|
||||
/* encoding */
|
||||
|
||||
#define JSON_INDENT(n) (n & 0x1F)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
#define JSON_SORT_KEYS 0x80
|
||||
#define JSON_PRESERVE_ORDER 0x100
|
||||
#define JSON_ENCODE_ANY 0x200
|
||||
#define JSON_ESCAPE_SLASH 0x400
|
||||
#define JSON_MAX_INDENT 0x1F
|
||||
#define JSON_INDENT(n) ((n) & JSON_MAX_INDENT)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
#define JSON_SORT_KEYS 0x80
|
||||
#define JSON_PRESERVE_ORDER 0x100
|
||||
#define JSON_ENCODE_ANY 0x200
|
||||
#define JSON_ESCAPE_SLASH 0x400
|
||||
#define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11)
|
||||
|
||||
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2014 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -49,6 +49,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
size_t length;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -64,9 +65,13 @@ typedef struct {
|
||||
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
/* Create a string by taking ownership of an existing buffer */
|
||||
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
|
||||
|
||||
/* Error message formatting */
|
||||
void jsonp_error_init(json_error_t *error, const char *source);
|
||||
void jsonp_error_set_source(json_error_t *error, const char *source);
|
||||
void jsonp_error_set(json_error_t *error, int line, int column,
|
||||
@@ -76,13 +81,14 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
|
||||
/* Locale independent string<->double conversions */
|
||||
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value);
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value, int prec);
|
||||
|
||||
/* Wrappers for custom memory functions */
|
||||
void* jsonp_malloc(size_t size);
|
||||
void jsonp_free(void *ptr);
|
||||
char *jsonp_strndup(const char *str, size_t length);
|
||||
char *jsonp_strdup(const char *str);
|
||||
char *jsonp_strndup(const char *str, size_t len);
|
||||
|
||||
/* Windows compatibility */
|
||||
#ifdef _WIN32
|
||||
|
||||
+67
-39
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -63,7 +63,10 @@ typedef struct {
|
||||
strbuffer_t saved_text;
|
||||
int token;
|
||||
union {
|
||||
char *string;
|
||||
struct {
|
||||
char *val;
|
||||
size_t len;
|
||||
} string;
|
||||
json_int_t integer;
|
||||
double real;
|
||||
} value;
|
||||
@@ -279,6 +282,13 @@ static void lex_save_cached(lex_t *lex)
|
||||
}
|
||||
}
|
||||
|
||||
static void lex_free_string(lex_t *lex)
|
||||
{
|
||||
jsonp_free(lex->value.string.val);
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
|
||||
/* assumes that str points to 'u' plus at least 4 valid hex digits */
|
||||
static int32_t decode_unicode_escape(const char *str)
|
||||
{
|
||||
@@ -297,7 +307,7 @@ static int32_t decode_unicode_escape(const char *str)
|
||||
else if(l_isupper(c))
|
||||
value += c - 'A' + 10;
|
||||
else
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -310,7 +320,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
char *t;
|
||||
int i;
|
||||
|
||||
lex->value.string = NULL;
|
||||
lex->value.string.val = NULL;
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
@@ -365,14 +375,12 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
- two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
|
||||
are converted to 4 bytes
|
||||
*/
|
||||
lex->value.string = jsonp_malloc(lex->saved_text.length + 1);
|
||||
if(!lex->value.string) {
|
||||
t = jsonp_malloc(lex->saved_text.length + 1);
|
||||
if(!t) {
|
||||
/* this is not very nice, since TOKEN_INVALID is returned */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* the target */
|
||||
t = lex->value.string;
|
||||
lex->value.string.val = t;
|
||||
|
||||
/* + 1 to skip the " */
|
||||
p = strbuffer_value(&lex->saved_text) + 1;
|
||||
@@ -381,17 +389,24 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
if(*p == '\\') {
|
||||
p++;
|
||||
if(*p == 'u') {
|
||||
char buffer[4];
|
||||
int length;
|
||||
size_t length;
|
||||
int32_t value;
|
||||
|
||||
value = decode_unicode_escape(p);
|
||||
if(value < 0) {
|
||||
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
|
||||
goto out;
|
||||
}
|
||||
p += 5;
|
||||
|
||||
if(0xD800 <= value && value <= 0xDBFF) {
|
||||
/* surrogate pair */
|
||||
if(*p == '\\' && *(p + 1) == 'u') {
|
||||
int32_t value2 = decode_unicode_escape(++p);
|
||||
if(value2 < 0) {
|
||||
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
|
||||
goto out;
|
||||
}
|
||||
p += 5;
|
||||
|
||||
if(0xDC00 <= value2 && value2 <= 0xDFFF) {
|
||||
@@ -420,16 +435,9 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
error_set(error, lex, "invalid Unicode '\\u%04X'", value);
|
||||
goto out;
|
||||
}
|
||||
else if(value == 0)
|
||||
{
|
||||
error_set(error, lex, "\\u0000 is not allowed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(utf8_encode(value, buffer, &length))
|
||||
if(utf8_encode(value, t, &length))
|
||||
assert(0);
|
||||
|
||||
memcpy(t, buffer, length);
|
||||
t += length;
|
||||
}
|
||||
else {
|
||||
@@ -451,11 +459,12 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
*(t++) = *(p++);
|
||||
}
|
||||
*t = '\0';
|
||||
lex->value.string.len = t - lex->value.string.val;
|
||||
lex->token = TOKEN_STRING;
|
||||
return;
|
||||
|
||||
out:
|
||||
jsonp_free(lex->value.string);
|
||||
lex_free_string(lex);
|
||||
}
|
||||
|
||||
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
|
||||
@@ -474,7 +483,7 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
{
|
||||
const char *saved_text;
|
||||
char *end;
|
||||
double value;
|
||||
double doubleval;
|
||||
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
@@ -499,16 +508,16 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
}
|
||||
|
||||
if(c != '.' && c != 'E' && c != 'e') {
|
||||
json_int_t value;
|
||||
json_int_t intval;
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
|
||||
errno = 0;
|
||||
value = json_strtoint(saved_text, &end, 10);
|
||||
intval = json_strtoint(saved_text, &end, 10);
|
||||
if(errno == ERANGE) {
|
||||
if(value < 0)
|
||||
if(intval < 0)
|
||||
error_set(error, lex, "too big negative integer");
|
||||
else
|
||||
error_set(error, lex, "too big integer");
|
||||
@@ -518,7 +527,7 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
assert(end == saved_text + lex->saved_text.length);
|
||||
|
||||
lex->token = TOKEN_INTEGER;
|
||||
lex->value.integer = value;
|
||||
lex->value.integer = intval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -552,13 +561,13 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
|
||||
if(jsonp_strtod(&lex->saved_text, &value)) {
|
||||
if(jsonp_strtod(&lex->saved_text, &doubleval)) {
|
||||
error_set(error, lex, "real number overflow");
|
||||
goto out;
|
||||
}
|
||||
|
||||
lex->token = TOKEN_REAL;
|
||||
lex->value.real = value;
|
||||
lex->value.real = doubleval;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@@ -571,10 +580,8 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
|
||||
strbuffer_clear(&lex->saved_text);
|
||||
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
jsonp_free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
if(lex->token == TOKEN_STRING)
|
||||
lex_free_string(lex);
|
||||
|
||||
c = lex_get(lex, error);
|
||||
while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
@@ -635,13 +642,14 @@ out:
|
||||
return lex->token;
|
||||
}
|
||||
|
||||
static char *lex_steal_string(lex_t *lex)
|
||||
static char *lex_steal_string(lex_t *lex, size_t *out_len)
|
||||
{
|
||||
char *result = NULL;
|
||||
if(lex->token == TOKEN_STRING)
|
||||
{
|
||||
result = lex->value.string;
|
||||
lex->value.string = NULL;
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
result = lex->value.string.val;
|
||||
*out_len = lex->value.string.len;
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -659,7 +667,7 @@ static int lex_init(lex_t *lex, get_func get, void *data)
|
||||
static void lex_close(lex_t *lex)
|
||||
{
|
||||
if(lex->token == TOKEN_STRING)
|
||||
jsonp_free(lex->value.string);
|
||||
lex_free_string(lex);
|
||||
strbuffer_close(&lex->saved_text);
|
||||
}
|
||||
|
||||
@@ -680,6 +688,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
|
||||
while(1) {
|
||||
char *key;
|
||||
size_t len;
|
||||
json_t *value;
|
||||
|
||||
if(lex->token != TOKEN_STRING) {
|
||||
@@ -687,9 +696,14 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = lex_steal_string(lex);
|
||||
key = lex_steal_string(lex, &len);
|
||||
if(!key)
|
||||
return NULL;
|
||||
if (memchr(key, '\0', len)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "NUL byte in object key not supported");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(flags & JSON_REJECT_DUPLICATES) {
|
||||
if(json_object_get(object, key)) {
|
||||
@@ -788,7 +802,21 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
|
||||
switch(lex->token) {
|
||||
case TOKEN_STRING: {
|
||||
json = json_string_nocheck(lex->value.string);
|
||||
const char *value = lex->value.string.val;
|
||||
size_t len = lex->value.string.len;
|
||||
|
||||
if(!(flags & JSON_ALLOW_NUL)) {
|
||||
if(memchr(value, '\0', len)) {
|
||||
error_set(error, lex, "\\u0000 is not allowed without JSON_ALLOW_NUL");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
json = jsonp_stringn_nocheck_own(value, len);
|
||||
if(json) {
|
||||
lex->value.string.val = NULL;
|
||||
lex->value.string.len = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
|
||||
These are functions for producing 32-bit hashes for hash table lookup.
|
||||
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
||||
are externally useful functions. Routines to test the hash are included
|
||||
if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||
the public domain. It has no warranty.
|
||||
|
||||
You probably want to use hashlittle(). hashlittle() and hashbig()
|
||||
hash byte arrays. hashlittle() is is faster than hashbig() on
|
||||
little-endian machines. Intel and AMD are little-endian machines.
|
||||
On second thought, you probably want hashlittle2(), which is identical to
|
||||
hashlittle() except it returns two 32-bit hashes for the price of one.
|
||||
You could implement hashbig2() if you wanted but I haven't bothered here.
|
||||
|
||||
If you want to find a hash of, say, exactly 7 integers, do
|
||||
a = i1; b = i2; c = i3;
|
||||
mix(a,b,c);
|
||||
a += i4; b += i5; c += i6;
|
||||
mix(a,b,c);
|
||||
a += i7;
|
||||
final(a,b,c);
|
||||
then use c as the hash value. If you have a variable length array of
|
||||
4-byte integers to hash, use hashword(). If you have a byte array (like
|
||||
a character string), use hashlittle(). If you have several byte arrays, or
|
||||
a mix of things, see the comments above hashlittle().
|
||||
|
||||
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
|
||||
then mix those integers. This is fast (you can do a lot more thorough
|
||||
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
|
||||
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h> /* defines uint32_t etc */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h> /* attempt to define endianness */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ENDIAN_H
|
||||
# include <endian.h> /* attempt to define endianness */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* My best guess at if you are big-endian or little-endian. This may
|
||||
* need adjustment.
|
||||
*/
|
||||
#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
|
||||
__BYTE_ORDER == __LITTLE_ENDIAN) || \
|
||||
(defined(i386) || defined(__i386__) || defined(__i486__) || \
|
||||
defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
|
||||
# define HASH_LITTLE_ENDIAN 1
|
||||
# define HASH_BIG_ENDIAN 0
|
||||
#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
|
||||
__BYTE_ORDER == __BIG_ENDIAN) || \
|
||||
(defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
|
||||
# define HASH_LITTLE_ENDIAN 0
|
||||
# define HASH_BIG_ENDIAN 1
|
||||
#else
|
||||
# define HASH_LITTLE_ENDIAN 0
|
||||
# define HASH_BIG_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#define hashsize(n) ((uint32_t)1<<(n))
|
||||
#define hashmask(n) (hashsize(n)-1)
|
||||
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
mix -- mix 3 32-bit values reversibly.
|
||||
|
||||
This is reversible, so any information in (a,b,c) before mix() is
|
||||
still in (a,b,c) after mix().
|
||||
|
||||
If four pairs of (a,b,c) inputs are run through mix(), or through
|
||||
mix() in reverse, there are at least 32 bits of the output that
|
||||
are sometimes the same for one pair and different for another pair.
|
||||
This was tested for:
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
|
||||
satisfy this are
|
||||
4 6 8 16 19 4
|
||||
9 15 3 18 27 15
|
||||
14 9 3 7 17 3
|
||||
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
||||
for "differ" defined as + with a one-bit base and a two-bit delta. I
|
||||
used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
||||
the operations, constants, and arrangements of the variables.
|
||||
|
||||
This does not achieve avalanche. There are input bits of (a,b,c)
|
||||
that fail to affect some output bits of (a,b,c), especially of a. The
|
||||
most thoroughly mixed value is c, but it doesn't really even achieve
|
||||
avalanche in c.
|
||||
|
||||
This allows some parallelism. Read-after-writes are good at doubling
|
||||
the number of bits affected, so the goal of mixing pulls in the opposite
|
||||
direction as the goal of parallelism. I did what I could. Rotates
|
||||
seem to cost as much as shifts on every machine I could lay my hands
|
||||
on, and rotates are much kinder to the top and bottom bits, so I used
|
||||
rotates.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define mix(a,b,c) \
|
||||
{ \
|
||||
a -= c; a ^= rot(c, 4); c += b; \
|
||||
b -= a; b ^= rot(a, 6); a += c; \
|
||||
c -= b; c ^= rot(b, 8); b += a; \
|
||||
a -= c; a ^= rot(c,16); c += b; \
|
||||
b -= a; b ^= rot(a,19); a += c; \
|
||||
c -= b; c ^= rot(b, 4); b += a; \
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
final -- final mixing of 3 32-bit values (a,b,c) into c
|
||||
|
||||
Pairs of (a,b,c) values differing in only a few bits will usually
|
||||
produce values of c that look totally different. This was tested for
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
These constants passed:
|
||||
14 11 25 16 4 14 24
|
||||
12 14 25 16 4 14 24
|
||||
and these came close:
|
||||
4 8 15 26 3 22 24
|
||||
10 8 15 26 3 22 24
|
||||
11 8 15 26 3 22 24
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define final(a,b,c) \
|
||||
{ \
|
||||
c ^= b; c -= rot(b,14); \
|
||||
a ^= c; a -= rot(c,11); \
|
||||
b ^= a; b -= rot(a,25); \
|
||||
c ^= b; c -= rot(b,16); \
|
||||
a ^= c; a -= rot(c,4); \
|
||||
b ^= a; b -= rot(a,14); \
|
||||
c ^= b; c -= rot(b,24); \
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
hashlittle() -- hash a variable-length key into a 32-bit value
|
||||
k : the key (the unaligned variable-length array of bytes)
|
||||
length : the length of the key, counting by bytes
|
||||
initval : can be any 4-byte value
|
||||
Returns a 32-bit value. Every bit of the key affects every bit of
|
||||
the return value. Two keys differing by one or two bits will have
|
||||
totally different hash values.
|
||||
|
||||
The best hash table sizes are powers of 2. There is no need to do
|
||||
mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
use a bitmask. For example, if you need only 10 bits, do
|
||||
h = (h & hashmask(10));
|
||||
In which case, the hash table should have hashsize(10) elements.
|
||||
|
||||
If you are hashing n strings (uint8_t **)k, do it like this:
|
||||
for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
|
||||
|
||||
By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
|
||||
code any way you wish, private, educational, or commercial. It's free.
|
||||
|
||||
Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||
acceptable. Do NOT use for cryptographic purposes.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
|
||||
{
|
||||
uint32_t a,b,c; /* internal state */
|
||||
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
|
||||
|
||||
/* Set up the internal state */
|
||||
a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
|
||||
|
||||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||
|
||||
/* Detect Valgrind or AddressSanitizer */
|
||||
#ifdef VALGRIND
|
||||
# define NO_MASKING_TRICK 1
|
||||
#else
|
||||
# if defined(__has_feature) /* Clang */
|
||||
# if __has_feature(address_sanitizer) /* is ASAN enabled? */
|
||||
# define NO_MASKING_TRICK 1
|
||||
# endif
|
||||
# else
|
||||
# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */
|
||||
# define NO_MASKING_TRICK 1
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef NO_MASKING_TRICK
|
||||
const uint8_t *k8;
|
||||
#endif
|
||||
|
||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
/*
|
||||
* "k[2]&0xffffff" actually reads beyond the end of the string, but
|
||||
* then masks off the part it's not allowed to read. Because the
|
||||
* string is aligned, the masked-off tail is in the same word as the
|
||||
* rest of the string. Every machine with memory protection I've seen
|
||||
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||
* still catch it and complain. The masking trick does make the hash
|
||||
* noticably faster for short strings (like English words).
|
||||
*/
|
||||
#ifndef NO_MASKING_TRICK
|
||||
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
|
||||
case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
|
||||
case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
|
||||
case 8 : b+=k[1]; a+=k[0]; break;
|
||||
case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
|
||||
case 6 : b+=k[1]&0xffff; a+=k[0]; break;
|
||||
case 5 : b+=k[1]&0xff; a+=k[0]; break;
|
||||
case 4 : a+=k[0]; break;
|
||||
case 3 : a+=k[0]&0xffffff; break;
|
||||
case 2 : a+=k[0]&0xffff; break;
|
||||
case 1 : a+=k[0]&0xff; break;
|
||||
case 0 : return c; /* zero length strings require no mixing */
|
||||
}
|
||||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[1]; a+=k[0]; break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]; break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
|
||||
case 1 : a+=k8[0]; break;
|
||||
case 0 : return c;
|
||||
}
|
||||
|
||||
#endif /* !valgrind */
|
||||
|
||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||
const uint8_t *k8;
|
||||
|
||||
/*--------------- all but last block: aligned reads and different mixing */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0] + (((uint32_t)k[1])<<16);
|
||||
b += k[2] + (((uint32_t)k[3])<<16);
|
||||
c += k[4] + (((uint32_t)k[5])<<16);
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 6;
|
||||
}
|
||||
|
||||
/*----------------------------- handle the last (probably partial) block */
|
||||
k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||
case 10: c+=k[4];
|
||||
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 9 : c+=k8[8]; /* fall through */
|
||||
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||
case 6 : b+=k[2];
|
||||
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 5 : b+=k8[4]; /* fall through */
|
||||
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
|
||||
break;
|
||||
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||
case 2 : a+=k[0];
|
||||
break;
|
||||
case 1 : a+=k8[0];
|
||||
break;
|
||||
case 0 : return c; /* zero length requires no mixing */
|
||||
}
|
||||
|
||||
} else { /* need to read the key one byte at a time */
|
||||
const uint8_t *k = (const uint8_t *)key;
|
||||
|
||||
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||
while (length > 12)
|
||||
{
|
||||
a += k[0];
|
||||
a += ((uint32_t)k[1])<<8;
|
||||
a += ((uint32_t)k[2])<<16;
|
||||
a += ((uint32_t)k[3])<<24;
|
||||
b += k[4];
|
||||
b += ((uint32_t)k[5])<<8;
|
||||
b += ((uint32_t)k[6])<<16;
|
||||
b += ((uint32_t)k[7])<<24;
|
||||
c += k[8];
|
||||
c += ((uint32_t)k[9])<<8;
|
||||
c += ((uint32_t)k[10])<<16;
|
||||
c += ((uint32_t)k[11])<<24;
|
||||
mix(a,b,c);
|
||||
length -= 12;
|
||||
k += 12;
|
||||
}
|
||||
|
||||
/*-------------------------------- last block: affect all 32 bits of (c) */
|
||||
switch(length) /* all the case statements fall through */
|
||||
{
|
||||
case 12: c+=((uint32_t)k[11])<<24;
|
||||
case 11: c+=((uint32_t)k[10])<<16;
|
||||
case 10: c+=((uint32_t)k[9])<<8;
|
||||
case 9 : c+=k[8];
|
||||
case 8 : b+=((uint32_t)k[7])<<24;
|
||||
case 7 : b+=((uint32_t)k[6])<<16;
|
||||
case 6 : b+=((uint32_t)k[5])<<8;
|
||||
case 5 : b+=k[4];
|
||||
case 4 : a+=((uint32_t)k[3])<<24;
|
||||
case 3 : a+=((uint32_t)k[2])<<16;
|
||||
case 2 : a+=((uint32_t)k[1])<<8;
|
||||
case 1 : a+=k[0];
|
||||
break;
|
||||
case 0 : return c;
|
||||
}
|
||||
}
|
||||
|
||||
final(a,b,c);
|
||||
return c;
|
||||
}
|
||||
+12
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify it
|
||||
@@ -12,6 +12,10 @@
|
||||
#include "jansson.h"
|
||||
#include "jansson_private.h"
|
||||
|
||||
/* C89 allows these to be macros */
|
||||
#undef malloc
|
||||
#undef free
|
||||
|
||||
/* memory function pointers */
|
||||
static json_malloc_t do_malloc = malloc;
|
||||
static json_free_t do_free = free;
|
||||
@@ -34,18 +38,19 @@ void jsonp_free(void *ptr)
|
||||
|
||||
char *jsonp_strdup(const char *str)
|
||||
{
|
||||
char *new_str;
|
||||
size_t len;
|
||||
return jsonp_strndup(str, strlen(str));
|
||||
}
|
||||
|
||||
len = strlen(str);
|
||||
if(len == (size_t)-1)
|
||||
return NULL;
|
||||
char *jsonp_strndup(const char *str, size_t len)
|
||||
{
|
||||
char *new_str;
|
||||
|
||||
new_str = jsonp_malloc(len + 1);
|
||||
if(!new_str)
|
||||
return NULL;
|
||||
|
||||
memcpy(new_str, str, len + 1);
|
||||
memcpy(new_str, str, len);
|
||||
new_str[len] = '\0';
|
||||
return new_str;
|
||||
}
|
||||
|
||||
|
||||
+72
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
@@ -125,19 +125,18 @@ static json_t *pack(scanner_t *s, va_list *ap);
|
||||
/* ours will be set to 1 if jsonp_free() must be called for the result
|
||||
afterwards */
|
||||
static char *read_string(scanner_t *s, va_list *ap,
|
||||
const char *purpose, int *ours)
|
||||
const char *purpose, size_t *out_len, int *ours)
|
||||
{
|
||||
char t;
|
||||
strbuffer_t strbuff;
|
||||
const char *str;
|
||||
size_t length;
|
||||
char *result;
|
||||
|
||||
next_token(s);
|
||||
t = token(s);
|
||||
prev_token(s);
|
||||
|
||||
if(t != '#' && t != '+') {
|
||||
if(t != '#' && t != '%' && t != '+') {
|
||||
/* Optimize the simple case */
|
||||
str = va_arg(*ap, const char *);
|
||||
|
||||
@@ -146,11 +145,14 @@ static char *read_string(scanner_t *s, va_list *ap,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!utf8_check_string(str, -1)) {
|
||||
length = strlen(str);
|
||||
|
||||
if(!utf8_check_string(str, length)) {
|
||||
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = length;
|
||||
*ours = 0;
|
||||
return (char *)str;
|
||||
}
|
||||
@@ -170,6 +172,9 @@ static char *read_string(scanner_t *s, va_list *ap,
|
||||
if(token(s) == '#') {
|
||||
length = va_arg(*ap, int);
|
||||
}
|
||||
else if(token(s) == '%') {
|
||||
length = va_arg(*ap, size_t);
|
||||
}
|
||||
else {
|
||||
prev_token(s);
|
||||
length = strlen(str);
|
||||
@@ -188,15 +193,15 @@ static char *read_string(scanner_t *s, va_list *ap,
|
||||
}
|
||||
}
|
||||
|
||||
result = strbuffer_steal_value(&strbuff);
|
||||
|
||||
if(!utf8_check_string(result, -1)) {
|
||||
if(!utf8_check_string(strbuff.value, strbuff.length)) {
|
||||
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = strbuff.length;
|
||||
*ours = 1;
|
||||
return result;
|
||||
return strbuffer_steal_value(&strbuff);
|
||||
}
|
||||
|
||||
static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
@@ -206,6 +211,7 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
|
||||
while(token(s) != '}') {
|
||||
char *key;
|
||||
size_t len;
|
||||
int ours;
|
||||
json_t *value;
|
||||
|
||||
@@ -219,15 +225,19 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = read_string(s, ap, "object key", &ours);
|
||||
key = read_string(s, ap, "object key", &len, &ours);
|
||||
if(!key)
|
||||
goto error;
|
||||
|
||||
next_token(s);
|
||||
|
||||
value = pack(s, ap);
|
||||
if(!value)
|
||||
if(!value) {
|
||||
if(ours)
|
||||
jsonp_free(key);
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(json_object_set_new_nocheck(object, key, value)) {
|
||||
if(ours)
|
||||
@@ -290,20 +300,20 @@ static json_t *pack(scanner_t *s, va_list *ap)
|
||||
case '[':
|
||||
return pack_array(s, ap);
|
||||
|
||||
case 's': { /* string */
|
||||
case 's': /* string */
|
||||
{
|
||||
char *str;
|
||||
size_t len;
|
||||
int ours;
|
||||
json_t *result;
|
||||
|
||||
str = read_string(s, ap, "string", &ours);
|
||||
str = read_string(s, ap, "string", &len, &ours);
|
||||
if(!str)
|
||||
return NULL;
|
||||
|
||||
result = json_string_nocheck(str);
|
||||
if(ours)
|
||||
jsonp_free(str);
|
||||
|
||||
return result;
|
||||
if (ours)
|
||||
return jsonp_stringn_nocheck_own(str, len);
|
||||
else
|
||||
return json_stringn_nocheck(str, len);
|
||||
}
|
||||
|
||||
case 'n': /* null */
|
||||
@@ -340,6 +350,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
{
|
||||
int ret = -1;
|
||||
int strict = 0;
|
||||
int gotopt = 0;
|
||||
|
||||
/* Use a set (emulated by a hashtable) to check that all object
|
||||
keys are accessed. Checking that the correct number of keys
|
||||
@@ -396,7 +407,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
next_token(s);
|
||||
|
||||
if(token(s) == '?') {
|
||||
opt = 1;
|
||||
opt = gotopt = 1;
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
@@ -422,10 +433,26 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
if(strict == 0 && (s->flags & JSON_STRICT))
|
||||
strict = 1;
|
||||
|
||||
if(root && strict == 1 && key_set.size != json_object_size(root)) {
|
||||
long diff = (long)json_object_size(root) - (long)key_set.size;
|
||||
set_error(s, "<validation>", "%li object item(s) left unpacked", diff);
|
||||
goto out;
|
||||
if(root && strict == 1) {
|
||||
/* We need to check that all non optional items have been parsed */
|
||||
const char *key;
|
||||
json_t *value;
|
||||
long unpacked = 0;
|
||||
if (gotopt) {
|
||||
/* We have optional keys, we need to iter on each key */
|
||||
json_object_foreach(root, key, value) {
|
||||
if(!hashtable_get(&key_set, key)) {
|
||||
unpacked++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* No optional keys, we can just compare the number of items */
|
||||
unpacked = (long)json_object_size(root) - (long)key_set.size;
|
||||
}
|
||||
if (unpacked) {
|
||||
set_error(s, "<validation>", "%li object item(s) left unpacked", unpacked);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@@ -523,16 +550,32 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||
}
|
||||
|
||||
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||
const char **target;
|
||||
const char **str_target;
|
||||
size_t *len_target = NULL;
|
||||
|
||||
target = va_arg(*ap, const char **);
|
||||
if(!target) {
|
||||
str_target = va_arg(*ap, const char **);
|
||||
if(!str_target) {
|
||||
set_error(s, "<args>", "NULL string argument");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(root)
|
||||
*target = json_string_value(root);
|
||||
next_token(s);
|
||||
|
||||
if(token(s) == '%') {
|
||||
len_target = va_arg(*ap, size_t *);
|
||||
if(!len_target) {
|
||||
set_error(s, "<args>", "NULL string length argument");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
prev_token(s);
|
||||
|
||||
if(root) {
|
||||
*str_target = json_string_value(root);
|
||||
if(len_target)
|
||||
*len_target = json_string_length(root);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
+9
-5
@@ -2,12 +2,13 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
|
||||
/* need config.h to get the correct snprintf */
|
||||
/* need jansson_private_config.h to get the correct snprintf */
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#if JSON_HAVE_LOCALECONV
|
||||
@@ -69,7 +70,7 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out)
|
||||
value = strtod(strbuffer->value, &end);
|
||||
assert(end == strbuffer->value + strbuffer->length);
|
||||
|
||||
if(errno == ERANGE && value != 0) {
|
||||
if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
|
||||
/* Overflow */
|
||||
return -1;
|
||||
}
|
||||
@@ -78,13 +79,16 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value)
|
||||
int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
|
||||
{
|
||||
int ret;
|
||||
char *start, *end;
|
||||
size_t length;
|
||||
|
||||
ret = snprintf(buffer, size, "%.17g", value);
|
||||
if (precision == 0)
|
||||
precision = 17;
|
||||
|
||||
ret = snprintf(buffer, size, "%.*g", precision, value);
|
||||
if(ret < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
+13
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <string.h>
|
||||
#include "utf.h"
|
||||
|
||||
int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
|
||||
{
|
||||
if(codepoint < 0)
|
||||
return -1;
|
||||
@@ -44,7 +44,7 @@ int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int utf8_check_first(char byte)
|
||||
size_t utf8_check_first(char byte)
|
||||
{
|
||||
unsigned char u = (unsigned char)byte;
|
||||
|
||||
@@ -80,9 +80,9 @@ int utf8_check_first(char byte)
|
||||
}
|
||||
}
|
||||
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
int32_t value = 0;
|
||||
unsigned char u = (unsigned char)buffer[0];
|
||||
|
||||
@@ -136,12 +136,12 @@ int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
|
||||
{
|
||||
int count;
|
||||
size_t count;
|
||||
int32_t value;
|
||||
|
||||
if(!*buffer)
|
||||
if(!bufsize)
|
||||
return buffer;
|
||||
|
||||
count = utf8_check_first(buffer[0]);
|
||||
@@ -152,7 +152,7 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
value = (unsigned char)buffer[0];
|
||||
else
|
||||
{
|
||||
if(!utf8_check_full(buffer, count, &value))
|
||||
if(count > bufsize || !utf8_check_full(buffer, count, &value))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -162,21 +162,18 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
return buffer + count;
|
||||
}
|
||||
|
||||
int utf8_check_string(const char *string, int length)
|
||||
int utf8_check_string(const char *string, size_t length)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(length == -1)
|
||||
length = strlen(string);
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < length; i++)
|
||||
{
|
||||
int count = utf8_check_first(string[i]);
|
||||
size_t count = utf8_check_first(string[i]);
|
||||
if(count == 0)
|
||||
return 0;
|
||||
else if(count > 1)
|
||||
{
|
||||
if(i + count > length)
|
||||
if(count > length - i)
|
||||
return 0;
|
||||
|
||||
if(!utf8_check_full(&string[i], count, NULL))
|
||||
|
||||
+11
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,31 +9,19 @@
|
||||
#define UTF_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
/* inttypes.h includes stdint.h in a standard environment, so there's
|
||||
no need to include stdint.h separately. If inttypes.h doesn't define
|
||||
int32_t, it's defined in config.h. */
|
||||
#include <inttypes.h>
|
||||
#endif /* HAVE_INTTYPES_H */
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#else /* !HAVE_CONFIG_H */
|
||||
#ifdef _WIN32
|
||||
typedef int int32_t;
|
||||
#else /* !_WIN32 */
|
||||
/* Assume a standard environment */
|
||||
#include <inttypes.h>
|
||||
#endif /* _WIN32 */
|
||||
int utf8_encode(int32_t codepoint, char *buffer, size_t *size);
|
||||
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
size_t utf8_check_first(char byte);
|
||||
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint);
|
||||
|
||||
int utf8_encode(int codepoint, char *buffer, int *size);
|
||||
|
||||
int utf8_check_first(char byte);
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint);
|
||||
|
||||
int utf8_check_string(const char *string, int length);
|
||||
int utf8_check_string(const char *string, size_t length);
|
||||
|
||||
#endif
|
||||
+111
-20
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2013 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,11 +9,19 @@
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <jansson_private_config.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
#include "jansson_private.h"
|
||||
@@ -21,8 +29,10 @@
|
||||
|
||||
/* Work around nonstandard isnan() and isinf() implementations */
|
||||
#ifndef isnan
|
||||
#ifndef __sun
|
||||
static JSON_INLINE int isnan(double x) { return x != x; }
|
||||
#endif
|
||||
#endif
|
||||
#ifndef isinf
|
||||
static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
|
||||
#endif
|
||||
@@ -36,11 +46,19 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
|
||||
|
||||
/*** object ***/
|
||||
|
||||
extern volatile uint32_t hashtable_seed;
|
||||
|
||||
json_t *json_object(void)
|
||||
{
|
||||
json_object_t *object = jsonp_malloc(sizeof(json_object_t));
|
||||
if(!object)
|
||||
return NULL;
|
||||
|
||||
if (!hashtable_seed) {
|
||||
/* Autoseed */
|
||||
json_object_seed(0);
|
||||
}
|
||||
|
||||
json_init(&object->json, JSON_OBJECT);
|
||||
|
||||
if(hashtable_init(&object->hashtable))
|
||||
@@ -76,7 +94,7 @@ json_t *json_object_get(const json_t *json, const char *key)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
if(!key || !json_is_object(json))
|
||||
return NULL;
|
||||
|
||||
object = json_to_object(json);
|
||||
@@ -108,7 +126,7 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
|
||||
int json_object_set_new(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
if(!key || !utf8_check_string(key, -1))
|
||||
if(!key || !utf8_check_string(key, strlen(key)))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
@@ -121,7 +139,7 @@ int json_object_del(json_t *json, const char *key)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
if(!key || !json_is_object(json))
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
@@ -620,33 +638,68 @@ static json_t *json_array_deep_copy(const json_t *array)
|
||||
|
||||
/*** string ***/
|
||||
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
static json_t *string_create(const char *value, size_t len, int own)
|
||||
{
|
||||
char *v;
|
||||
json_string_t *string;
|
||||
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
string = jsonp_malloc(sizeof(json_string_t));
|
||||
if(!string)
|
||||
return NULL;
|
||||
json_init(&string->json, JSON_STRING);
|
||||
if(own)
|
||||
v = (char *)value;
|
||||
else {
|
||||
v = jsonp_strndup(value, len);
|
||||
if(!v)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string->value = jsonp_strdup(value);
|
||||
if(!string->value) {
|
||||
jsonp_free(string);
|
||||
string = jsonp_malloc(sizeof(json_string_t));
|
||||
if(!string) {
|
||||
if(!own)
|
||||
jsonp_free(v);
|
||||
return NULL;
|
||||
}
|
||||
json_init(&string->json, JSON_STRING);
|
||||
string->value = v;
|
||||
string->length = len;
|
||||
|
||||
return &string->json;
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
return json_string_nocheck(value);
|
||||
return string_create(value, strlen(value), 0);
|
||||
}
|
||||
|
||||
json_t *json_stringn_nocheck(const char *value, size_t len)
|
||||
{
|
||||
return string_create(value, len, 0);
|
||||
}
|
||||
|
||||
/* this is private; "steal" is not a public API concept */
|
||||
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len)
|
||||
{
|
||||
return string_create(value, len, 1);
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
{
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
return json_stringn(value, strlen(value));
|
||||
}
|
||||
|
||||
json_t *json_stringn(const char *value, size_t len)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, len))
|
||||
return NULL;
|
||||
|
||||
return json_stringn_nocheck(value, len);
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *json)
|
||||
@@ -657,7 +710,23 @@ const char *json_string_value(const json_t *json)
|
||||
return json_to_string(json)->value;
|
||||
}
|
||||
|
||||
size_t json_string_length(const json_t *json)
|
||||
{
|
||||
if(!json_is_string(json))
|
||||
return 0;
|
||||
|
||||
return json_to_string(json)->length;
|
||||
}
|
||||
|
||||
int json_string_set_nocheck(json_t *json, const char *value)
|
||||
{
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
return json_string_setn_nocheck(json, value, strlen(value));
|
||||
}
|
||||
|
||||
int json_string_setn_nocheck(json_t *json, const char *value, size_t len)
|
||||
{
|
||||
char *dup;
|
||||
json_string_t *string;
|
||||
@@ -665,23 +734,32 @@ int json_string_set_nocheck(json_t *json, const char *value)
|
||||
if(!json_is_string(json) || !value)
|
||||
return -1;
|
||||
|
||||
dup = jsonp_strdup(value);
|
||||
dup = jsonp_strndup(value, len);
|
||||
if(!dup)
|
||||
return -1;
|
||||
|
||||
string = json_to_string(json);
|
||||
jsonp_free(string->value);
|
||||
string->value = dup;
|
||||
string->length = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_string_set(json_t *json, const char *value)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
return json_string_set_nocheck(json, value);
|
||||
return json_string_setn(json, value, strlen(value));
|
||||
}
|
||||
|
||||
int json_string_setn(json_t *json, const char *value, size_t len)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, len))
|
||||
return -1;
|
||||
|
||||
return json_string_setn_nocheck(json, value, len);
|
||||
}
|
||||
|
||||
static void json_delete_string(json_string_t *string)
|
||||
@@ -692,12 +770,25 @@ static void json_delete_string(json_string_t *string)
|
||||
|
||||
static int json_string_equal(json_t *string1, json_t *string2)
|
||||
{
|
||||
return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
|
||||
json_string_t *s1, *s2;
|
||||
|
||||
if(!json_is_string(string1) || !json_is_string(string2))
|
||||
return 0;
|
||||
|
||||
s1 = json_to_string(string1);
|
||||
s2 = json_to_string(string2);
|
||||
return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length);
|
||||
}
|
||||
|
||||
static json_t *json_string_copy(const json_t *string)
|
||||
{
|
||||
return json_string_nocheck(json_string_value(string));
|
||||
json_string_t *s;
|
||||
|
||||
if(!json_is_string(string))
|
||||
return NULL;
|
||||
|
||||
s = json_to_string(string);
|
||||
return json_stringn_nocheck(s->value, s->length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -614,6 +614,8 @@
|
||||
918A074C1804B4A600983AE1 /* libjansson.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 918A07221804AFAA00983AE1 /* libjansson.a */; };
|
||||
918A074D1804B4A600983AE1 /* libmongoose.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 918A07141804AF0F00983AE1 /* libmongoose.a */; };
|
||||
9193B29817EDFEB7003CC0BA /* profiling.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9193B29717EDFEB7003CC0BA /* profiling.cpp */; };
|
||||
91F4EE3A1AB32675006EF158 /* hashtable_seed.c in Sources */ = {isa = PBXBuildFile; fileRef = 91F4EE381AB32675006EF158 /* hashtable_seed.c */; };
|
||||
91F4EE3B1AB32675006EF158 /* lookup3.h in Headers */ = {isa = PBXBuildFile; fileRef = 91F4EE391AB32675006EF158 /* lookup3.h */; };
|
||||
F007DDF612F4F44600D2F6C1 /* jconfig.h in Headers */ = {isa = PBXBuildFile; fileRef = F007DDB512F4F44600D2F6C1 /* jconfig.h */; };
|
||||
F007DDF712F4F44600D2F6C1 /* jdct.h in Headers */ = {isa = PBXBuildFile; fileRef = F007DDB612F4F44600D2F6C1 /* jdct.h */; };
|
||||
F007DDF912F4F44600D2F6C1 /* jerror.h in Headers */ = {isa = PBXBuildFile; fileRef = F007DDB812F4F44600D2F6C1 /* jerror.h */; };
|
||||
@@ -1556,6 +1558,8 @@
|
||||
918A07361804B01800983AE1 /* utf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utf.h; sourceTree = "<group>"; };
|
||||
918A07371804B01800983AE1 /* value.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = value.c; sourceTree = "<group>"; };
|
||||
9193B29717EDFEB7003CC0BA /* profiling.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = profiling.cpp; sourceTree = "<group>"; };
|
||||
91F4EE381AB32675006EF158 /* hashtable_seed.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hashtable_seed.c; sourceTree = "<group>"; };
|
||||
91F4EE391AB32675006EF158 /* lookup3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lookup3.h; sourceTree = "<group>"; };
|
||||
B2F67ED704C74A3F00A80002 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
|
||||
B2F67F1E04C773E400A80002 /* GLUT.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLUT.framework; path = /System/Library/Frameworks/GLUT.framework; sourceTree = "<absolute>"; };
|
||||
F007DDB512F4F44600D2F6C1 /* jconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jconfig.h; sourceTree = "<group>"; };
|
||||
@@ -3116,6 +3120,8 @@
|
||||
918A07261804B01800983AE1 /* jansson */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
91F4EE381AB32675006EF158 /* hashtable_seed.c */,
|
||||
91F4EE391AB32675006EF158 /* lookup3.h */,
|
||||
918A07271804B01800983AE1 /* dump.c */,
|
||||
918A07281804B01800983AE1 /* error.c */,
|
||||
918A07291804B01800983AE1 /* hashtable.c */,
|
||||
@@ -3544,6 +3550,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
918A07461804B01800983AE1 /* utf.h in Headers */,
|
||||
91F4EE3B1AB32675006EF158 /* lookup3.h in Headers */,
|
||||
918A073B1804B01800983AE1 /* hashtable.h in Headers */,
|
||||
918A073E1804B01800983AE1 /* jansson_private.h in Headers */,
|
||||
918A07431804B01800983AE1 /* strbuffer.h in Headers */,
|
||||
@@ -4219,6 +4226,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
91F4EE3A1AB32675006EF158 /* hashtable_seed.c in Sources */,
|
||||
918A07451804B01800983AE1 /* utf.c in Sources */,
|
||||
918A07391804B01800983AE1 /* error.c in Sources */,
|
||||
918A07441804B01800983AE1 /* strconv.c in Sources */,
|
||||
@@ -4587,6 +4595,13 @@
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
_DEBUG,
|
||||
SCP_UNIX,
|
||||
USE_OPENAL,
|
||||
APPLE_APP,
|
||||
HAVE_STDINT_H,
|
||||
);
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
@@ -4599,6 +4614,13 @@
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
NDEBUG,
|
||||
SCP_UNIX,
|
||||
USE_OPENAL,
|
||||
APPLE_APP,
|
||||
HAVE_STDINT_H,
|
||||
);
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user