Issue in fetching float attributes in Sphinx using Node.js - node.js

I have been trying to fetch sphinx data using Node.js and limestone. I got everything from sphinx instead the float value. In my Sphinx index Height is defined as float value ("sql_attr_float = Height") and but the Node.js returning some integer value.
Eg: if Height value in sphinx is 172.72, then i got "1127004242" from the Node.js
Please help me on this.
Below is the function used in limestone file which reads sphinx data,
proto.toReader = function toReader() {
var offset = 0, length = this.length, buffer = this;
return {
empty: function empty() {
return offset >= length;
},
int64: function shiftInt64() {
var hi_low_pair = buffer.int64Read(offset);
offset += 8;
return hi_low_pair;
},
int32: function shiftInt32() {
var number = buffer.int32Read(offset);
offset += 4;
return number;
},
int16: function shiftInt16() {
var number = buffer.int16Read(offset);
offset += 2;
return number;
},
buffer: function shiftBuffer(length) {
var b = buffer.slice(offset, offset + length);
offset += length;
return b;
},
string: function shiftString(length) {
var string = buffer.toString('utf8', offset, offset + length);
offset += length;
return string;
},
cstring: function shiftCstring() {
var end = offset;
while (buffer[end] > 0 && end < length) { end++; }
var string = buffer.toString('utf8', offset, end);
offset = end + 1;
return string;
},
lstring: function shiftLString() {
var length = buffer.int32Read(offset);
offset += 4;
if(!isNaN(length) && !isNaN(offset)){
length = length;
var string = buffer.toString('utf8', offset, offset + length);
}else{
var string = "";
}
offset += length;
return string;
},
multicstring: function shiftMulticstring() {
var fields = [];
while (buffer[offset] > 0) {
fields.push(this.cstring());
}
offset++;
return fields;
},
hash: function shiftHash() {
var hash = {};
while (buffer[offset] > 0) {
hash[this.cstring()] = this.cstring();
}
offset++;
return hash;
}
};
};

Hmm, line 715, of
https://github.com/kurokikaze/limestone/blob/master/limestone.js
has
// FLOAT size attributes (32 bits)
if (output.attributes[attribute].type == Sphinx.attribute.FLOAT) {
attr_value = response.int32();
match.attrs[output.attributes[attribute].name] = attr_value;
continue;
}
So its just reading the float as int32!
And as you say toReader/makeWriter is missing float methods.
So you need to figure out how to decode that value yourself, and either patch limestone to directly handle floats, or just do it yourself in the application.
Checking SphinxAPI code, this is how it done in php:
// handle floats
if ( $type==SPH_ATTR_FLOAT )
{
list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
list(,$fval) = unpack ( "f*", pack ( "L", $uval ) );
$attrvals[$attr] = $fval;
continue;
}
java can do it somewhat nativly
/* handle floats */
if ( type==SPH_ATTR_FLOAT )
{
docInfo.attrValues.add ( attrNumber, new Float ( in.readFloat() ) );
continue;
}
For ports of pack/unpack:
pack / unpack functions for node.js

Related

In place string manipulation for character count

Given a string, do in place replacement of every character with it's immediate count.
eg: "aaabbbcc" - "a3b3c2"
"abab" - "a1b1a1b1"
You can do something like below
function getResult(input) {
if(!input) {
return ''; // if string is empty, return ''
}
if (input.length === 1) { // if string length is 1, return input + '1'
return input + '1'
}
var inputLength = input.length; // total length of the input string
var preChar = input[0]; // track previous character
var count = 1; // count of the previous character
for (var i = 1; i < inputLength; i++) {
if (preChar === input[i]) { // previous char and current char match
preChar = input[i]; // update prevChar with current char
count++; // count of prevChar is incremented
if (i + 1 < inputLength) { // not the end of the string
input = input.substr(0, i) + '#' + input.substr(i + 1); // update with dummy char '#' to maintain the length of the string, later will be removed
} else { // if it is the end of the string, update with count
input = input.substr(0, i) + count + input.substr(i + 1);
}
continue;
}
// if current char is not matched with prevChar
preChar = input[i]; // update the preChar with current char
// update the input with prevChar count and current char
input = input.substr(0, i) + count + input[i] + input.substr(i + 1);
inputLength++; // new count is added in the string, so total length of the string is increased by one
i++; // increment the i also because total length is increased
count = 1; // update the count with 1
if (i + 1 === inputLength) { // if it is last element
input = input.substr(0, i+1) + count + input.substr(i + 2);
}
}
input = input.replace(/\#/g, '') // remove dummy char '#' with ''
return input; // return the input
}
console.log("result for aaabbbcc is ", getResult('aaabbbcc'))
console.log("result for abab is ", getResult('abab'))
This answer assumes the OP uses ASCII
Analysis of the requirement of the the OP
Analysis of the bytes required:
When there is 1 repeat only, 2 bytes are needed.
When there are 2 repeats, 2 bytes are needed.
When there are 3, 2 bytes are needed.
When there are 9, 2 bytes.
When there are 99, 3 bytes.
When there are 999, 4 bytes.
When there are (2^64 - 1) repeats, only 20 bytes are needed.
As you can see, the bytes required to express the integer are log10(N)(at least 1) where N is the number of repeats.
Repeat-once
So, when there is only 1 repeat and there is no spare space, it has to keep iterating until
sequence that have more bytes than needed(have at least 3 repeats) to give it space
to reallocate when the end is reached
(In a string where most repeat at least twice or thrice and those who only repeat once are not gathered in one place, this is seldom required. This algorithm makes this assumption).
Then, the string will be modified backward to prevent any overwritten of data. This work because for each repeat-once sequence, it will take up double bytes to store them, so when the sequence at i will be written at 2i.
C++14 Code
#include <algorithm>
#include <cassert>
#include <utility>
#include <type_traits>
// Definition of helper functions for converting ints to string(raw and no need for format string).
template <class size_t, class UnsignedInt,
class = std::enable_if_t<std::is_unsigned<UnsignedInt>::value>>
size_t to_str_len(UnsignedInt i) noexcept
{
size_t cnt = 0;
do {
++cnt;
i /= 10;
} while (i > 0);
return cnt;
}
/*
* out should either points to the beginning of array or the last.
* step should be either 1 or -1.
*/
template <class RandomAccessIt, class UnsignedInt,
class = std::enable_if_t<std::is_unsigned<UnsignedInt>::value>>
auto uitos_impl(RandomAccessIt out, int step, UnsignedInt i) noexcept
{
do {
*out = static_cast<char>(i % 10) + 48;
out += step;
i /= 10;
} while (i > 0);
return out;
}
template <class BidirectionalIt>
void reverse_str(BidirectionalIt beg, BidirectionalIt end) noexcept
{
do {
std::iter_swap(beg++, --end);
} while (beg < end);
}
template <class RandomAccessIt, class UnsignedInt>
auto uitos(RandomAccessIt beg, UnsignedInt i) noexcept
{
auto ret = uitos_impl(beg, 1, i);
// Reverse the string to get the right order
reverse_str(beg, ret);
return ret;
}
template <class RanIt, class UnsignedInt>
auto r_uitos(RanIt last, UnsignedInt i) noexcept
{
return uitos_impl(last, -1, i);
}
template <class size_t, class RandomAccessIt>
size_t count_repeat(RandomAccessIt beg, RandomAccessIt end) noexcept
{
auto first = beg;
auto &val = *beg;
do {
++beg;
} while (beg != end && *beg == val);
return beg - first;
}
template <class size_t, class RandomAccessIt>
size_t r_count_repeat(RandomAccessIt last) noexcept
{
auto it = last;
auto &val = *it;
do {
--it;
} while (*it == val);
return last - it;
}
template <class string,
class size_type = typename string::size_type>
struct character_count
{
static_assert(std::is_unsigned<size_type>::value,
"size_type should be unsigned");
static_assert(!std::is_empty<size_type>::value,
"size_type should not be empty");
private:
using str_size_t = typename string::size_type;
using str_it = typename string::iterator;
static_assert(std::is_same<typename string::value_type, char>::value,
"Only support ASCII");
static_assert(sizeof(size_type) <= sizeof(str_size_t),
"size_type should not be bigger than typename string::size_type");
string str;
str_it in;
str_it out;
str_it end;
size_type len;
void cnt_repeat() noexcept
{
assert(in != end);
len = count_repeat<size_type>(in, str.end());
}
void advance_in() noexcept
{
assert(in != end);
in += len;
assert(in <= end);
}
void process_impl()
{
assert(in != end);
assert(out <= in);
// The main loop
do {
cnt_repeat();
if (len > 1 || out < in) {
*out = *in;
out = uitos(out + 1, len);
advance_in();
assert(out <= in);
} else {
auto first = find_enough_space();
auto deduced_first = write_backward();
assert(first == deduced_first);
}
} while (in != end);
}
auto find_enough_space()
{
assert(out == in);
auto first = in;
size_type bytes_required = 0;
do {
cnt_repeat();
advance_in();
bytes_required += to_str_len<size_type>(len) + 1;
if (size_type(in - first) >= bytes_required) {
out = first + bytes_required;
return first;
}
} while (in != str.end());
auto first_offset = first - str.begin();
// Hopefully this path won't be executed.
auto new_size = first_offset + bytes_required;
auto original_size = str.size();
assert(new_size > original_size);
str.resize(new_size);
first = str.begin() + first_offset;
out = str.end();
in = str.begin() + original_size;
end = str.begin() + original_size;
return first;
}
auto write_backward() noexcept
{
auto original_out = out--;
auto original_in = in--;
do {
len = r_count_repeat<size_type>(in);
out = r_uitos(out, len);
*out-- = *((in -= len) + 1);
} while (out != in);
auto ret = out + 1;
out = original_out;
in = original_in;
return ret;
}
public:
character_count(string &&arg):
str(std::move(arg)), in(str.begin()), out(str.begin()), end(str.end())
{}
character_count(const string &arg):
str(arg), in(str.begin()), out(str.begin()), end(str.end())
{}
/*
* ```str``` should not be empty and should not have non-visible character
*/
auto& process()
{
assert(!str.empty());
process_impl();
str.erase(out, str.end());
return *this;
}
auto& get_result() & noexcept
{
return str;
}
auto&& get_result() && noexcept
{
return std::move(str);
}
auto& get_result() const& noexcept
{
return str;
}
/*
* ```str``` should not be empty and should not have non-visible character
*/
auto& set_string(const string &arg) noexcept
{
str = arg;
in = str.begin();
out = str.begin();
end = str.end();
return *this;
}
/*
* ```str``` should not be empty and should not have non-visible character
*/
auto& set_string(string &&arg) noexcept
{
str = std::move(arg);
in = str.begin();
out = str.begin();
end = str.end();
return *this;
}
};
Assuming you're using Javascript, this is how I would do it. Just match all the groups with a regex and loop over them:
function charCount (str) {
const exp = /(\w)\1*/g
let matches = (str).match(exp)
let output = ''
matches.forEach(group => {
output += group[0] + group.length
})
return output
}
const str = 'abab'
const str2 = 'aaabbbcc'
console.log(charCount(str)) // a1b1a1b1
console.log(charCount(str2)) // a3b3c2

How to create sourcemaps for concatenated files

I want to concatenate a bunch of different files of a single type into one large file. For example, many javascript files into one large file, many css files down to one etc. I want to create a sourcemap of the files pre concatenation, but I do not know where to start. I am working in Node, but I am also open to solutions in other environments.
I know there are tools that can do this, but they seem to be on a language by language basis (uglifyjs, cssmin or whatever its called these days), but I want a tool that is not language specific.
Also, I would like to define how the files are bound. For example, in javascript I want to give each file its own closure with an IIFE. Such as:
(function () {
// File
}());
I can also think of other wrappers I would like to implement for different files.
Here are my options as I see it right now. However, I don't know which is best or how to start any of them.
Find a module that does this (I'm working in a Node.js environment)
Create an algorithm with Mozilla's source-map module. For that I also see a couple options.
Only map each line to the new line location
Map every single character to the new location
Map every word to its new location (this options seems way out of scope)
Don't even worry about source maps
What do you guys think about these options. I've already tried options 2.1 and 2.2, but the solution seemed way too complicated for a concatenation algorithm and it did not perform perfectly in the Google Chrome browser tools.
I implemented code without any dependencies like this:
export interface SourceMap {
version: number; // always 3
file?: string;
sourceRoot?: string;
sources: string[];
sourcesContent?: string[];
names?: string[];
mappings: string | Buffer;
}
const emptySourceMap: SourceMap = { version: 3, sources: [], mappings: new Buffer(0) }
var charToInteger = new Buffer(256);
var integerToChar = new Buffer(64);
charToInteger.fill(255);
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split('').forEach((char, i) => {
charToInteger[char.charCodeAt(0)] = i;
integerToChar[i] = char.charCodeAt(0);
});
class DynamicBuffer {
buffer: Buffer;
size: number;
constructor() {
this.buffer = new Buffer(512);
this.size = 0;
}
ensureCapacity(capacity: number) {
if (this.buffer.length >= capacity)
return;
let oldBuffer = this.buffer;
this.buffer = new Buffer(Math.max(oldBuffer.length * 2, capacity));
oldBuffer.copy(this.buffer);
}
addByte(b: number) {
this.ensureCapacity(this.size + 1);
this.buffer[this.size++] = b;
}
addVLQ(num: number) {
var clamped: number;
if (num < 0) {
num = (-num << 1) | 1;
} else {
num <<= 1;
}
do {
clamped = num & 31;
num >>= 5;
if (num > 0) {
clamped |= 32;
}
this.addByte(integerToChar[clamped]);
} while (num > 0);
}
addString(s: string) {
let l = Buffer.byteLength(s);
this.ensureCapacity(this.size + l);
this.buffer.write(s, this.size);
this.size += l;
}
addBuffer(b: Buffer) {
this.ensureCapacity(this.size + b.length);
b.copy(this.buffer, this.size);
this.size += b.length;
}
toBuffer(): Buffer {
return this.buffer.slice(0, this.size);
}
}
function countNL(b: Buffer): number {
let res = 0;
for (let i = 0; i < b.length; i++) {
if (b[i] === 10) res++;
}
return res;
}
export class SourceMapBuilder {
outputBuffer: DynamicBuffer;
sources: string[];
mappings: DynamicBuffer;
lastSourceIndex = 0;
lastSourceLine = 0;
lastSourceCol = 0;
constructor() {
this.outputBuffer = new DynamicBuffer();
this.mappings = new DynamicBuffer();
this.sources = [];
}
addLine(text: string) {
this.outputBuffer.addString(text);
this.outputBuffer.addByte(10);
this.mappings.addByte(59); // ;
}
addSource(content: Buffer, sourceMap?: SourceMap) {
if (sourceMap == null) sourceMap = emptySourceMap;
this.outputBuffer.addBuffer(content);
let sourceLines = countNL(content);
if (content.length > 0 && content[content.length - 1] !== 10) {
sourceLines++;
this.outputBuffer.addByte(10);
}
let sourceRemap = [];
sourceMap.sources.forEach((v) => {
let pos = this.sources.indexOf(v);
if (pos < 0) {
pos = this.sources.length;
this.sources.push(v);
}
sourceRemap.push(pos);
});
let lastOutputCol = 0;
let inputMappings = (typeof sourceMap.mappings === "string") ? new Buffer(<string>sourceMap.mappings) : <Buffer>sourceMap.mappings;
let outputLine = 0;
let ip = 0;
let inOutputCol = 0;
let inSourceIndex = 0;
let inSourceLine = 0;
let inSourceCol = 0;
let shift = 0;
let value = 0;
let valpos = 0;
const commit = () => {
if (valpos === 0) return;
this.mappings.addVLQ(inOutputCol - lastOutputCol);
lastOutputCol = inOutputCol;
if (valpos === 1) {
valpos = 0;
return;
}
let outSourceIndex = sourceRemap[inSourceIndex];
this.mappings.addVLQ(outSourceIndex - this.lastSourceIndex);
this.lastSourceIndex = outSourceIndex;
this.mappings.addVLQ(inSourceLine - this.lastSourceLine);
this.lastSourceLine = inSourceLine;
this.mappings.addVLQ(inSourceCol - this.lastSourceCol);
this.lastSourceCol = inSourceCol;
valpos = 0;
}
while (ip < inputMappings.length) {
let b = inputMappings[ip++];
if (b === 59) { // ;
commit();
this.mappings.addByte(59);
inOutputCol = 0;
lastOutputCol = 0;
outputLine++;
} else if (b === 44) { // ,
commit();
this.mappings.addByte(44);
} else {
b = charToInteger[b];
if (b === 255) throw new Error("Invalid sourceMap");
value += (b & 31) << shift;
if (b & 32) {
shift += 5;
} else {
let shouldNegate = value & 1;
value >>= 1;
if (shouldNegate) value = -value;
switch (valpos) {
case 0: inOutputCol += value; break;
case 1: inSourceIndex += value; break;
case 2: inSourceLine += value; break;
case 3: inSourceCol += value; break;
}
valpos++;
value = shift = 0;
}
}
}
commit();
while (outputLine < sourceLines) {
this.mappings.addByte(59);
outputLine++;
}
}
toContent(): Buffer {
return this.outputBuffer.toBuffer();
}
toSourceMap(sourceRoot?: string): Buffer {
return new Buffer(JSON.stringify({ version: 3, sourceRoot, sources: this.sources, mappings: this.mappings.toBuffer().toString() }));
}
}
I, at first, implemented "index map" from that spec, only to find out that it is not supported by any browser.
Another project that could be useful to look at is magic string.

How to read non-byte-aligned integers with Node.js?

I am trying to read a binary SWF file using Node.JS. As the specification mentions at the bottom of the 17th page, some integers are encoded using variable-length bit fields, and by definition most of them are not byte-aligned.
The problem is that Node.js's Buffer only provides functions for reading byte-aligned integers. So I tried to write a wrapper object that would read bit by bit. However, it seems really hacky. Below is the object prototype I wrote:
/*jslint node:true, bitwise:true */
'use strict';
var util = require('util');
var maxBits = 32;
function BitBuffer() {
Buffer.apply(this, arguments);
this.cursor = 0;
this.bitCursor = 0;
}
util.inherits(BitBuffer, Buffer);
module.exports = BitBuffer;
function padBits(bits, length, bit) {
var leftPad = '', repeatLength, i;
bits = bits.toString(2);
length = length || 8;
bit = bit || '0';
if (bits.length >= length) {
return bits;
} else {
repeatLength = length - bits.length;
for (i = 0; i < repeatLength; i += 1) {
leftPad += bit;
}
return leftPad + bits;
}
}
BitBuffer.prototype.move = function (bits) {
var bytes = Math.floor((this.bitCursor + bits) / 8);
this.bitCursor += bits;
if (this.bitCursor > 8) {
this.cursor += bytes;
this.bitCursor -= bytes * 8;
}
if (this.cursor >= this.length) {
this.rewind();
return false;
}
return true;
};
BitBuffer.prototype.align = function () {
if (this.bitCursor > 0) {
this.bitCursor = 0;
this.cursor += 1;
}
};
BitBuffer.prototype.rewind = function () {
this.cursor = this.bitCursor = 0;
};
BitBuffer.prototype.readBits = function (bits) {
var bytes = Math.ceil((this.bitCursor + bits) / 8), result = 0,
length, buffer, i;
if (bits > maxBits) {
throw new RangeError('Cannot read more than ' + maxBits + ' bits');
}
buffer = this.slice(this.cursor, this.cursor + bytes);
result = padBits(buffer[0]).substr(this.bitCursor);
length = buffer.length;
for (i = 1; i < length; i += 1) {
result += padBits(buffer[i]);
}
result = result.substr(0, bits);
return (this.move(bits)) ? parseInt(result, 2) : false;
};
BitBuffer.prototype.readUB = BitBuffer.prototype.readBits;
BitBuffer.prototype.readSB = function (bits) {
var readBits = this.readBits(bits),
stringBits = readBits.toString(2);
if (readBits === false) {
return false;
}
// add automatically removed zeros
if (stringBits.length < bits) {
stringBits = padBits(stringBits, bits);
}
// negative, pad to 32 bits then invert bits
if (stringBits[0] === '1') {
return -~parseInt(padBits(stringBits, maxBits, '1'), 2) - 1;
} else {
return readBits;
}
};
BitBuffer.prototype.readFB = function (bits) {
var highBits, lowBits, result;
if (bits < 17) {
throw new RangeError('Should be at least 17 bits long');
}
highBits = this.readSB(bits - 16);
lowBits = this.readUB(16);
lowBits *= Math.pow(10, -lowBits.toString(10).length);
return (highBits >= 0) ?
highBits + lowBits : highBits - lowBits;
};
// wrap read functions
(function () {
var nativeReadFunctions = {
'readInt8': 8,
'readInt16LE': 16,
'readInt16BE': 16,
'readInt32LE': 32,
'readInt32BE': 32,
'readUInt8': 8,
'readUInt16LE': 16,
'readUInt16BE': 16,
'readUInt32LE': 32,
'readUInt32BE': 32,
'readDoubleLE': 64,
'readDoubleBE': 64,
'readFloatLE': 32,
'readFloatBE': 32
}, method;
function wrapNativeRead(method, length) {
return function (noAssert) {
var cursor;
this.align();
cursor = this.cursor;
this.move(length);
return Buffer.prototype[method].call(
this,
this.cursor,
noAssert
);
};
}
for (method in nativeReadFunctions) {
if (nativeReadFunctions.hasOwnProperty(method)) {
BitBuffer.prototype[method] =
wrapNativeRead(method, nativeReadFunctions[method]);
}
}
}());
Is writing my own object the good way?
Beside your implementation is pretty good, you should understand that in your implementation this.buffer === this and you should change the following lines:
// inside BitBuffer prototype constructor
this.buffer = new Buffer(param1, param2);
// change to
Buffer.call(this, param1, param2); // The right way to call the super constructor
and
// inside BitBuffer.prototype.readBits
buffer = this.buffer.slice(this.cursor, this.cursor + bytes);
// change to
buffer = this.slice(this.cursor, this.cursor + bytes);

removing a char from string in Unity3D

I am making a word game in unity3D game engine, I make a word if word spelling is wrong I want to remove a certain character from that word for making a correct spelling and then other character shift back... in my code I have an string and want to remove a char from string.... so that when the char is removed from the center of the string other char shift back.
static var nextPos = 200;
static var word: String;
var sel: String;
var isClicked : boolean=false;
var xpos: float = 200;
static var i:int=0;
function start()
{
word="";
}
function OnMouseDown()
{
if (!isClicked) {
isClicked = true;
xpos = nextPos;
sel=OnGUI();
word=word+sel;
nextPos += 8;
i++;
}
else if(isClicked)
{
isClicked = false;
xpos = nextPos;
nextPos -= 8;
}
}
function OnGUI()
{
if (gameObject.name == "Sphere(Clone)" && isClicked )
{
GUI.Label(new Rect(xpos,260,400,100), "A");
return "A";
}
else if (gameObject.name == "Sphere 1(Clone)" && isClicked )
{
GUI.Label(new Rect(xpos,260,400,100), "B");
return "B";
}
else if (gameObject.name == "Sphere 2(Clone)" && isClicked )
{
GUI.Label(new Rect(xpos,260,400,100), "C");
return "C";
}
GUI.Label(new Rect(xpos,280,400,100), "Value" + i);
GUI.Label(new Rect(xpos,300,400,100), word);
}
Test this:
string value = "abcde";
string temp="";
temp = value.Substring(0, position - 1);
value = temp + value.Substring(position, value.Length);
you can check which key is pressed once your text is being displayed
this should work . you need just call it in your update function or OnGUI function
set "word" as a global variable if you use it in an Update function
I wrote this out of my head , It should work , sorry it it has any glitches.
if(Input.anyKey)
{
string letter = Input.inputString;
if (word.Contains(letter))
{
word.Replace(letter,"");
}
}

Function failing to return a value

So I'm making a simple steganography tool (encrypting messages within images) and exposing it as a web service via Node.js. I am very new to Javascript and Node.js in particular. The app first converts a text string into a binary string by changing each character into an 8-bit ASCII encoding, resulting in one large binary string. I then encrypt the message within the pixels. Even values of pixels represent 0s from the binary, and odd values represent 1s. The end of the string is marked as 3 pixels of value 100 in a row (this is temporary, until I figure out a better way to mark the end). I'm using a node.js library called 'pngjs' that gives me pixel-level access to png images.
So I have a problem with the decodeMessage function. It builds up the string message, and is then meant to return it, however the return call at the end results in undefined.
How can I fix it?
Thanks in advance for the help!
function encodeMessage(image, mes) {
var message = mes;
var fs = require('fs'),
PNG = require('pngjs').PNG;
fs.createReadStream(image)
.pipe(new PNG({
filterType: 4
}))
.on('parsed', function() {
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x);// << 2;
//console.log(idx);
if (idx < message.length) {
var item = message.charAt(idx);
/* if the character in the encoded string is 0 */
if (item == 0) {
/* if the pixel is odd, we want it to be even */
if (this.data[idx] % 2 == 1) {
/* if the pixel is 0, add 1 to it */
if (this.data[idx] == 0) {
this.data[idx] = this.data[idx] + 1;
} else {
/* else substract 1 */
this.data[idx] = this.data[idx] - 1;
}
}
} else {
/* if the character in the encoded string is 1 */
if (this.data[idx] % 2 == 0) {
if (this.data[idx] == 0) {
this.data[idx] = this.data[idx] + 1;
} else {
this.data[idx] = this.data[idx] - 1;
}
}
}
//console.log(this.data[idx]);
} else if (idx === message.length) {
/* do something to the first pixel following the end of the string */
this.data[idx] = 100;
this.data[idx+1] = 100;
this.data[idx+2] = 100;
//console.log(this.data[idx]);
} else {
/* do something to the remaining pixels */
}
}
}
this.pack().pipe(fs.createWriteStream('encoded_' + image));
});
}
function decodeMessage(image) {
var message = "";
var fs = require('fs'),
PNG = require('pngjs').PNG;
fs.createReadStream(image)
.pipe(new PNG({
filterType: 4
}))
.on('parsed', function() {
dance:
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x);// << 2;
if (this.data[idx] == 100 && this.data[idx+1] == 100 && this.data[idx+2] == 100) {
break dance;
} else {
if (this.data[idx] % 2 == 0) {
message += "0";
} else {
message += "1";
}
}
}
}
/* the message outputs correctly over here */
console.log(message);
//return message;
});
/* but the return of the variable here doesn't work */
return message;
}
exports.encodeMessage = encodeMessage;
exports.decodeMessage = decodeMessage;
The parsed event is fired asynchronously, so you cannot return a value from decodeMessage.
function decodeMessage(image, cb) {
// Code
.on('parsed', function() {
// Code
console.log(message);
cb(message);
});
}
Then you must pass a callback to your decodeMessage function.
decodeMessage(image, function(decoded){
// Here is the decoded data.
});
The same is true for your encodeMessage function. The function will return before encoding has finished. If you want to know when it is done, you need to pass a callback the same way.

Resources