├── .editorconfig ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── LICENSE ├── benchmark ├── for.js ├── index.js ├── indexOf.js ├── split.js └── utils.js ├── package.json ├── readme.md └── test └── specifiers.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [*.json] 16 | indent_size = 2 17 | 18 | [*.yaml] 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | pids 12 | logs 13 | results 14 | 15 | npm-debug.log 16 | coverage 17 | node_modules 18 | .d8_history 19 | v8.log 20 | example.js 21 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "es5": false, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "immed": true, 6 | "latedef": true, 7 | "newcap": true, 8 | "noarg": true, 9 | "sub": true, 10 | "undef": true, 11 | "boss": true, 12 | "eqnull": true, 13 | "node": true, 14 | "jquery": true, 15 | "browser": true, 16 | "esnext": true, 17 | "bitwise": true, 18 | "camelcase": false, 19 | "expr": true, 20 | "regexp": true, 21 | "unused": true, 22 | "strict": true, 23 | "trailing": true, 24 | "smarttabs": true, 25 | "white": true, 26 | "quotmark": "single", 27 | "indent": 4, 28 | "maxerr": 5, 29 | "maxdepth": 5, 30 | "maxstatements": 25, 31 | "maxcomplexity": 25 32 | } 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | .DS_Store 3 | lib-cov 4 | coverage.html 5 | example.js 6 | v8.log 7 | .d8_history 8 | html-report 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - 'iojs' 5 | - '0.12' 6 | - '0.10' 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /benchmark/for.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function pff(str) { 4 | var strLen = str.length, 5 | a = 1, 6 | start = 0, 7 | end = -1, 8 | res = ''; 9 | 10 | for (var i = 0; i < strLen; i++) { 11 | if (str[i] !== '%') { 12 | continue; 13 | } 14 | 15 | end = i; 16 | var modifier = str[i + 1]; 17 | 18 | if (modifier === 's' || modifier === 'd') { 19 | var substitution = arguments[a++]; 20 | 21 | if (modifier === 'd') { 22 | substitution = Math.floor(substitution); 23 | } 24 | 25 | res += str.substring(start, end); 26 | res += substitution; 27 | start = i + 2; 28 | } 29 | 30 | if (modifier === '%') { 31 | res += str.substring(start, end); 32 | res += '%'; 33 | start = i + 2; 34 | i++; 35 | } 36 | } 37 | 38 | if (start < strLen) { 39 | res += str.substring(start); 40 | } 41 | 42 | return res; 43 | }; 44 | -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | /*global suite, bench */ 2 | 'use strict'; 3 | 4 | var utils = require('./utils.js'); 5 | 6 | function tests (func) { 7 | bench('tiny (prod)', function () { 8 | func('/i/%s/%s/%s/carousel.%d.jpg', 'some', 'folder', 'with', 5); 9 | }); 10 | 11 | bench('short (prod)', function () { 12 | func('feature.%s.%s.%s-feature.%s.%s.%s-feature.%s.%s.%s-feature.%s.%s.%s-feature.%s.%s.description', 'easyinterface', 'smartsearch', 'description', 'easyinterface', 'smartsearch', 'description', 'easyinterface', 'smartsearch', 'description', 'easyinterface', 'smartsearch', 'description', 'easyinterface', 'smartsearch'); 13 | }); 14 | 15 | bench('tiny (rand)', utils.makeBench(func, 5)); 16 | bench('short (rand)', utils.makeBench(func, 10)); 17 | } 18 | 19 | suite('for', tests.bind(null, require('./for.js'))); 20 | suite('split', tests.bind(null, require('./split.js'))); 21 | suite('indexOf', tests.bind(null, require('./indexOf.js'))); 22 | suite('util.format', tests.bind(null, require('util').format)); 23 | suite('sprint', tests.bind(null, require('sprint'))); 24 | suite('node-printf', tests.bind(null, require('printf'))); 25 | -------------------------------------------------------------------------------- /benchmark/indexOf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (ptrn) { 4 | var result = ''; 5 | var idx = 0; 6 | var lastIdx = -1; 7 | 8 | for (var current = 1; current < arguments.length; current++) { 9 | idx = ptrn.indexOf('%', lastIdx); 10 | if (idx === -1) { break; } 11 | if (ptrn[idx + 1] === 's' || ptrn[idx + 1] === 'd') { 12 | result += ptrn.substring(lastIdx, idx); 13 | result += (ptrn[idx + 1] === 'd') ? Math.floor(arguments[current]) : arguments[current]; 14 | lastIdx = idx + 2; 15 | } else if (ptrn[idx + 1] === '%') { 16 | result += ptrn.substring(lastIdx, idx + 1); 17 | lastIdx = idx + 2; 18 | current--; 19 | } else { 20 | result += ptrn.substring(lastIdx, idx + 2); 21 | lastIdx = idx + 2; 22 | current--; 23 | } 24 | } 25 | 26 | result = result + ptrn.substring(lastIdx); 27 | 28 | return result; 29 | }; 30 | -------------------------------------------------------------------------------- /benchmark/split.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function split(str) { 4 | var a = 1, 5 | res = ''; 6 | 7 | var parts = str.split('%'), 8 | len = parts.length; 9 | 10 | if (len > 0) { res += parts[0]; } 11 | 12 | for (var i = 1; i < len; i++) { 13 | if (parts[i][0] === 's' || parts[i][0] === 'd') { 14 | var value = arguments[a++]; 15 | res += parts[i][0] === 'd' ? Math.floor(value) : value; 16 | } else if (parts[i][0]) { 17 | res += '%' + parts[i][0]; 18 | } else { 19 | i++; 20 | res += '%' + parts[i][0]; 21 | } 22 | 23 | res += parts[i].substring(1); 24 | } 25 | 26 | return res; 27 | }; 28 | -------------------------------------------------------------------------------- /benchmark/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function getRandomString () { 4 | return (Math.random() * 100).toString(16); 5 | } 6 | 7 | function getRandomArgs (n) { 8 | var args = []; 9 | for (var i = 0; i < n; i++) { 10 | args.push(getRandomString()); 11 | } 12 | return args; 13 | } 14 | 15 | function getRandomPattern (n) { 16 | var pattern = ''; 17 | for (var i = 0; i < n; i++) { 18 | pattern += getRandomString() + '%s'; 19 | } 20 | pattern += getRandomString(); 21 | return pattern; 22 | } 23 | 24 | function makeBench(func, n) { 25 | var args = getRandomArgs(n); 26 | args.unshift(getRandomPattern(n)); 27 | return function () { 28 | func.apply(null, args); 29 | }; 30 | } 31 | 32 | module.exports = { 33 | getRandomString: getRandomString, 34 | getRandomArgs: getRandomArgs, 35 | getRandomPattern: getRandomPattern, 36 | makeBench: makeBench 37 | }; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pff", 3 | "version": "1.0.0", 4 | "description": "Minimal implementation of printf, which is really fast", 5 | "main": "benchmark/split.js", 6 | "files": [ 7 | "benchmark/split.js" 8 | ], 9 | "scripts": { 10 | "test": "mocha" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git@github.com:floatdrop/pff.git" 15 | }, 16 | "keywords": [ 17 | "printf", 18 | "format", 19 | "string" 20 | ], 21 | "author": "Vsevolod Strukchinsky ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/floatdrop/pff/issues" 25 | }, 26 | "homepage": "https://github.com/floatdrop/pff", 27 | "devDependencies": { 28 | "mocha": "~2.2.1", 29 | "should": "~5.2.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # pff [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] 2 | > Minimal printf implementation 3 | 4 | __No more words, show me the numbers!__ 5 | 6 | ![image](https://cloud.githubusercontent.com/assets/365089/3465905/427d6f24-0274-11e4-9348-216df8f05060.png) 7 | 8 | Run yourself to get numbers relevant to your hardware: 9 | 10 | ```bash 11 | $ npm i -g matcha printf sprint 12 | $ npm i 13 | $ matcha benchmark/index.js 14 | ``` 15 | 16 | ## Usage 17 | 18 | ```js 19 | var pff = require('pff'); 20 | 21 | console.log(pff('%s world from %d year!', 'Hello', 2014.7)); 22 | // Hello world from 2014 year! 23 | ``` 24 | 25 | ## Specifiers 26 | 27 | | Specifier | What it does | Example | Result | 28 | | ------------: | --------------------- | --------------------------- | ---------------- | 29 | | **%s** | String | `pff('Hello %s', 'world')` | `'Hello world'` | 30 | | **%d** | Floored number | `pff('My age is %d', 13.2)` | `'My age is 13'` | 31 | | **%%** | Percent | `pff('100%%s cool!')` | `'100%s cool!'` | 32 | 33 | Not much, but hey! - it's fast! 34 | 35 | # License 36 | 37 | MIT (c) 2014 Vsevolod Strukchinsky (floatdrop@gmail.com) 38 | 39 | [npm-url]: https://npmjs.org/package/pff 40 | [npm-image]: http://img.shields.io/npm/v/pff.svg 41 | 42 | [travis-url]: https://travis-ci.org/floatdrop/pff 43 | [travis-image]: http://img.shields.io/travis/floatdrop/pff.svg 44 | -------------------------------------------------------------------------------- /test/specifiers.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | 'use strict'; 4 | 5 | require('should'); 6 | var pff = require('..'); 7 | 8 | describe('pff', function () { 9 | it('should substitute short string right', function () { 10 | pff('/i/%s/%s/%s/carousel.%d.jpg', 'some', 'folder', 'with', 5) 11 | .should.equal('/i/some/folder/with/carousel.5.jpg'); 12 | }); 13 | 14 | it('should substitute medium string right', function () { 15 | pff('feature.%s.%s.%s-feature.%s.%s.%s-feature.%s.%s.%s-feature.%s.%s.%s-feature.%s.%s.description', 'easyinterface', 'smartsearch', 'description', 'easyinterface', 'smartsearch', 'description', 'easyinterface', 'smartsearch', 'description', 'easyinterface', 'smartsearch', 'description', 'easyinterface', 'smartsearch') 16 | .should.equal('feature.easyinterface.smartsearch.description-feature.easyinterface.smartsearch.description-feature.easyinterface.smartsearch.description-feature.easyinterface.smartsearch.description-feature.easyinterface.smartsearch.description'); 17 | }); 18 | 19 | it('should preserve % in pattern', function () { 20 | pff('%s % %s', 'one', 'two').should.equal('one % two'); 21 | }); 22 | 23 | it('should replace %% with %', function () { 24 | pff('%s %% %s', 'one', 'two').should.equal('one % two'); 25 | }); 26 | 27 | it('should substitute once in pattern', function () { 28 | pff('%s', 'result').should.equal('result'); 29 | }); 30 | 31 | it('should substitute twice in pattern', function () { 32 | pff('%s%s', 'one', 'two').should.equal('onetwo'); 33 | }); 34 | 35 | it('should substitute in the middle of pattern', function () { 36 | pff('one %s three', 'two').should.equal('one two three'); 37 | }); 38 | 39 | it('should substitute only patterns with defined values, otherwise - undefined', function () { 40 | pff('one two %s %s', 'three').should.equal('one two three undefined'); 41 | }); 42 | 43 | it('should ignore additional values for substitution', function () { 44 | pff('one two %s', 'three', 'four').should.equal('one two three'); 45 | }); 46 | 47 | it('should substitute %d', function () { 48 | pff('%d', 1).should.equal('1'); 49 | }); 50 | 51 | it('should substitute %d in the middle of pattern', function () { 52 | pff('one %d three', 2.4).should.equal('one 2 three'); 53 | }); 54 | 55 | it('should substitute %d with flooring', function () { 56 | pff('one %d three', 2.8).should.equal('one 2 three'); 57 | }); 58 | 59 | it('should substitute %% with %', function () { 60 | pff('one %%s three', 'wow').should.equal('one %s three'); 61 | }); 62 | 63 | it('should substitute undefined and null', function () { 64 | pff('one %s three', undefined).should.equal('one undefined three'); 65 | pff('one %s three', null).should.equal('one null three'); 66 | }); 67 | }); 68 | --------------------------------------------------------------------------------