marcjs
Version:
MARC record Node.js library
135 lines (124 loc) • 3.87 kB
JavaScript
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-console */
const { Duplex } = require('stream');
const Record = require('./Record');
class Iso2709Parser extends Duplex {
constructor(options = {}) {
const opts = {
readableObjectMode: true,
readableHighWaterMark: options.readableHighWaterMark || 200,
writableObjectMode: false,
writableHighWaterMark: options.writableHighWaterMark || 100000,
...options,
};
super(opts);
this.prevData = ''; // Le buffer précédent du stream en cours de lecture
this.prevStart = -1; // La position de ce qu'il reste à lire
this.count = 0;
this.records = [];
this.processing = false;
this.noMoreDataAvailable = false;
}
_read() { this._processNext(); }
_processNext() {
if (this.processing) return;
this.processing = true;
if (this.records.length > 0) {
const record = this.records.shift();
const canContinue = this.push(record);
if (!canContinue) {
this.processing = false;
return;
}
}
if (this.noMoreDataAvailable && this.records.length === 0) {
this.push(null);
} else {
setImmediate(() => {
this.processing = false;
this._processNext();
});
}
}
_final(callback) {
this.noMoreDataAvailable = true;
this.processing = false;
this._processNext();
callback();
}
_write(data, encoding, callback) {
let start = 0;
let pos = 0;
const len = data.length;
while (pos <= len) {
while (pos <= len && data[pos] !== 29) {
pos += 1;
}
if (pos <= len) {
let raw;
if (this.prevStart !== -1) {
const prevLen = this.prevData.length - this.prevStart;
raw = Buffer.alloc(prevLen + pos + 1);
this.prevData.copy(raw, 0, this.prevStart, this.prevData.length);
data.copy(raw, prevLen, 0, pos);
this.prevStart = -1;
} else {
raw = Buffer.alloc(pos - start + 1);
data.copy(raw, 0, start, pos);
}
const record = Iso2709Parser.parse(raw);
this.records.push(record);
this.count += 1;
pos += 1;
start = pos;
}
}
if (pos !== len) {
this.prevData = data;
this.prevStart = start;
} else {
this.prevStart = -1; // Marque qu'on n'a rien à garder du précédent buffer
}
this._backpressure(callback);
if (!this.processing) this._processNext();
}
_backpressure(callback) {
if (this.records.length < this.readableHighWaterMark) {
callback();
} else {
setTimeout(() => {
this._backpressure(callback);
}, 4);
}
}
static parse(data) {
const record = new Record();
record.leader = data.toString('utf8', 0, 24);
const directoryLen = parseInt(data.toString('utf8', 12, 17), 10) - 25;
const numberOfTag = directoryLen / 12;
record.fields = [];
for (let i = 0; i < numberOfTag; i += 1) {
const off = 24 + i * 12;
const tag = data.toString('utf8', off, off + 3);
const len = parseInt(data.toString('utf8', off + 3, off + 7), 10) - 1;
const pos = parseInt(data.toString('utf8', off + 7, off + 12), 10) + 25 + directoryLen;
let value = data.toString('utf-8', pos, pos + len);
const parts = [tag];
if (parseInt(tag, 10) < '010') {
parts.push(value);
} else if (value.indexOf('\x1F')) { // There are some letters
parts.push(value.substr(0, 2));
value = value.substr(2);
const values = value.split('\x1f');
for (let j = 1; j < values.length; j += 1) {
const v = values[j];
parts.push(v.substr(0, 1));
parts.push(v.substr(1));
}
}
record.fields.push(parts);
}
return record;
}
}
module.exports = Iso2709Parser;