Skip to content

Commit

Permalink
Add potential performance improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
bugventure committed Oct 4, 2016
1 parent 3a6c312 commit 5bdae5c
Showing 1 changed file with 101 additions and 80 deletions.
181 changes: 101 additions & 80 deletions lib/jsen.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,54 +80,10 @@ types.date = function (path) {
return path + ' instanceof Date';
};

keywords.type = function (context) {
if (!context.schema.type) {
return;
}

var specified = Array.isArray(context.schema.type) ? context.schema.type : [context.schema.type],
src = specified.map(function mapType(type) {
return types[type] ? types[type](context.path) : 'true';
}).join(' || ');

if (src) {
context.code('if (!(' + src + ')) {');
keywords.enum = function (context) {
var arr = context.schema['enum'];

context.error('type');

context.code('}');
}
};

keywords['enum'] = function (context) {
var arr = context.schema['enum'],
clauses = [],
value, enumType, i;

if (!Array.isArray(arr) || !arr.length) {
return;
}

for (i = 0; i < arr.length; i++) {
value = arr[i];
enumType = typeof value;

if (value === null || ['boolean', 'number', 'string'].indexOf(enumType) > -1) {
// simple equality check for simple data types
if (enumType === 'string') {
clauses.push(context.path + ' === ' + encodeStr(value));
}
else {
clauses.push(context.path + ' === ' + value);
}
}
else {
// deep equality check for complex types or regexes
clauses.push('equal(' + context.path + ', ' + JSON.stringify(value) + ')');
}
}

context.code('if (!(' + clauses.join(' || ') + ')) {');
context.code('if (!equalAny(' + context.path + ', ' + JSON.stringify(arr) + ')) {');
context.error('enum');
context.code('}');
};
Expand Down Expand Up @@ -556,46 +512,71 @@ keywords.not = function (context) {
('}');
};

function decorateGenerator(type, keyword) {
keywords[keyword].type = type;
keywords[keyword].keyword = keyword;
}

['minimum', 'exclusiveMinimum', 'maximum', 'exclusiveMaximum', 'multipleOf']
.forEach(function (keyword) { keywords[keyword].type = 'number'; });
.forEach(decorateGenerator.bind(null, 'number'));

['minLength', 'maxLength', 'pattern', 'format']
.forEach(function (keyword) { keywords[keyword].type = 'string'; });
.forEach(decorateGenerator.bind(null, 'string'));

['minItems', 'maxItems', 'additionalItems', 'uniqueItems', 'items']
.forEach(function (keyword) { keywords[keyword].type = 'array'; });
.forEach(decorateGenerator.bind(null, 'array'));

['maxProperties', 'minProperties', 'required', 'properties', 'patternProperties', 'additionalProperties', 'dependencies']
.forEach(function (keyword) { keywords[keyword].type = 'object'; });
.forEach(decorateGenerator.bind(null, 'object'));

['enum', 'allOf', 'anyOf', 'oneOf', 'not']
.forEach(decorateGenerator.bind(null, null));

function getGenerators(schema) {
function groupKeywords(schema) {
var keys = Object.keys(schema),
start = [],
perType = {},
gen, i;
ret = {
enum: schema.enum instanceof Array && schema.enum.length > 0,
type: null,
allType: [],
perType: {}
},
key, gen, i;

if (schema.type) {
if (typeof schema.type === 'string') {
ret.type = [schema.type];
}
else if (schema.type instanceof Array && schema.type.length) {
ret.type = schema.type.slice(0);
}
}

for (i = 0; i < keys.length; i++) {
gen = keywords[keys[i]];
key = keys[i];

if (key === 'enum' || key === 'type') {
continue;
}

gen = keywords[key];

if (!gen) {
continue;
}

if (gen.type) {
if (!perType[gen.type]) {
perType[gen.type] = [];
if (!ret.perType[gen.type]) {
ret.perType[gen.type] = [];
}

perType[gen.type].push(gen);
ret.perType[gen.type].push(key);
}
else {
start.push(gen);
ret.allType.push(key);
}
}

return start.concat(Object.keys(perType).reduce(function (arr, key) {
return arr.concat(perType[key]);
}, []));
return ret;
}

function getPathExpression(path, key) {
Expand Down Expand Up @@ -684,6 +665,16 @@ function clone(obj) {
return cloned;
}

function equalAny(obj, options) {
for (var i = 0, len = options.length; i < len; i++) {
if (equal(obj, options[i])) {
return true;
}
}

return false;
}

function PropertyMarker() {
this.objects = [];
this.properties = [];
Expand Down Expand Up @@ -813,7 +804,7 @@ function jsen(schema, options) {
errors: []
},
scope = {
equal: equal,
equalAny: equalAny,
unique: unique,
ucs2length: ucs2length,
refs: refs
Expand Down Expand Up @@ -863,10 +854,11 @@ function jsen(schema, options) {
pathExp,
index,
err,
lastType,
format,
gens,
gen,
schemaKeys,
typeKeys,
typeIndex,
validatedType,
i;

function error(keyword, key, additional) {
Expand Down Expand Up @@ -932,28 +924,57 @@ function jsen(schema, options) {
noFailFast: noFailFast
};

gens = getGenerators(schema);
schemaKeys = groupKeywords(schema);

for (i = 0; i < gens.length; i++) {
gen = gens[i];
if (schemaKeys.enum) {
keywords.enum(context);

if (gen.type && lastType !== gen.type) {
if (lastType) {
code('}');
}

lastType = gen.type;
return; // do not process the schema further
}

code('if (' + types[gen.type](path) + ') {');
}
typeKeys = Object.keys(schemaKeys.perType);

gen(context);
function generateForKeyword(keyword) {
keywords[keyword](context);
}

if (lastType) {
for (i = 0; i < typeKeys.length; i++) {
validatedType = typeKeys[i];

code((i ? 'else ' : '') + 'if (' + types[validatedType](path) + ') {');

schemaKeys.perType[validatedType].forEach(generateForKeyword);

code('}');

if (schemaKeys.type) {
typeIndex = schemaKeys.type.indexOf(validatedType);

if (typeIndex > -1) {
schemaKeys.type.splice(typeIndex, 1);
}
}
}

if (schemaKeys.type) { // we have types in the schema
if (schemaKeys.type.length) { // case 1: we still have some left to check
code((typeKeys.length ? 'else ' : '') + 'if (!(' + schemaKeys.type.map(function (type) {
return types[type] ? types[type](path) : 'true';
}).join(' || ') + ')) {');
error('type');
code('}');
}
else {
code('else {'); // case 2: we don't have any left to check
error('type');
code('}');
}
}

schemaKeys.allType.forEach(function (keyword) {
keywords[keyword](context);
});

if (schema.format && options.formats) {
format = options.formats[schema.format];

Expand Down

0 comments on commit 5bdae5c

Please sign in to comment.