Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement as class and port tests to Brittle #27

Merged
merged 7 commits into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
package-lock.json
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ random-access-file allows you to do just this.
## Usage

``` js
var randomAccessFile = require('random-access-file')
const RandomAccessFile = require('random-access-file')

var file = randomAccessFile('my-file.txt')
const file = new RandomAccessFile('my-file.txt')

file.write(10, Buffer.from('hello'), function(err) {
// write a buffer to offset 10
Expand All @@ -34,7 +34,7 @@ file will use an open file descriptor. When you are done with the file you shoul

## API

#### `var file = randomAccessFile(filename, [options])`
#### `const file = new RandomAccessFile(filename, [options])`

Create a new file. Options include:

Expand Down
10 changes: 5 additions & 5 deletions example.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
var raf = require('random-access-file')
var file = raf('hello.txt')
const RAF = require('.')
const file = new RAF('hello.txt')

var max = 500 * 1024 * 1024
var buf = Buffer.alloc(1024)
const max = 500 * 1024 * 1024
const buf = Buffer.alloc(1024)
buf.fill('lo')

var offset = 0
let offset = 0
write()

function write () {
Expand Down
270 changes: 133 additions & 137 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,176 +1,172 @@
var inherits = require('util').inherits
var RandomAccess = require('random-access-storage')
var fs = require('fs')
var mkdirp = require('mkdirp-classic')
var path = require('path')
var constants = fs.constants || require('constants') // eslint-disable-line

var READONLY = constants.O_RDONLY
var READWRITE = constants.O_RDWR | constants.O_CREAT

module.exports = RandomAccessFile

function RandomAccessFile (filename, opts) {
if (!(this instanceof RandomAccessFile)) return new RandomAccessFile(filename, opts)
RandomAccess.call(this)

if (!opts) opts = {}
if (opts.directory) filename = path.join(opts.directory, path.resolve('/', filename).replace(/^\w+:\\/, ''))

this.directory = opts.directory || null
this.filename = filename
this.fd = 0

// makes random-access-storage open in writable mode first
if (opts.writable || opts.truncate) this.preferReadonly = false

this._size = opts.size || opts.length || 0
this._truncate = !!opts.truncate || this._size > 0
this._rmdir = !!opts.rmdir
this._lock = opts.lock || noLock
this._sparse = opts.sparse || noLock
this._alloc = opts.alloc || Buffer.allocUnsafe
}
const RandomAccessStorage = require('random-access-storage')
const fs = require('fs')
const path = require('path')
const constants = fs.constants || require('constants') // eslint-disable-line n/no-deprecated-api

inherits(RandomAccessFile, RandomAccess)
const READONLY = constants.O_RDONLY
const READWRITE = constants.O_RDWR | constants.O_CREAT

RandomAccessFile.prototype._open = function (req) {
var self = this
module.exports = class RandomAccessFile extends RandomAccessStorage {
constructor (filename, opts = {}) {
super()

mkdirp(path.dirname(this.filename), ondir)
if (opts.directory) filename = path.join(opts.directory, path.resolve('/', filename).replace(/^\w+:\\/, ''))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does the regex do?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vs just path.join(opts.directory, filename)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it removes the protocol portion if passing a URL. It wasn't changed from master:

if (opts.directory) filename = path.join(opts.directory, path.resolve('/', filename).replace(/^\w+:\\/, ''))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, just a weird diff


function ondir (err) {
if (err) return req.callback(err)
open(self, READWRITE, req)
}
}
this.directory = opts.directory || null
this.filename = filename
this.fd = 0

RandomAccessFile.prototype._openReadonly = function (req) {
open(this, READONLY, req)
}
// makes random-access-storage open in writable mode first
if (opts.writable || opts.truncate) this.preferReadonly = false

RandomAccessFile.prototype._write = function (req) {
var data = req.data
var fd = this.fd

fs.write(fd, data, 0, req.size, req.offset, onwrite)
this._size = opts.size || opts.length || 0
this._truncate = !!opts.truncate || this._size > 0
this._rmdir = !!opts.rmdir
this._lock = opts.lock || noLock
this._sparse = opts.sparse || noLock
this._alloc = opts.alloc || Buffer.allocUnsafe
}

function onwrite (err, wrote) {
if (err) return req.callback(err)
_open (req) {
const self = this

req.size -= wrote
req.offset += wrote
fs.mkdir(path.dirname(this.filename), { recursive: true }, ondir)

if (!req.size) return req.callback(null)
fs.write(fd, data, data.length - req.size, req.size, req.offset, onwrite)
function ondir (err) {
if (err) return req.callback(err)
self._openMode(READWRITE, req)
}
}
}

RandomAccessFile.prototype._read = function (req) {
var self = this
var data = req.data || this._alloc(req.size)
var fd = this.fd
_openReadonly (req) {
this._openMode(READONLY, req)
}

if (!req.size) return process.nextTick(readEmpty, req)
fs.read(fd, data, 0, req.size, req.offset, onread)
_openMode (mode, req) {
const self = this

function onread (err, read) {
if (err) return req.callback(err)
if (!read) return req.callback(createReadError(self.filename, req.offset, req.size))
if (this.fd) fs.close(this.fd, oncloseold)
else fs.open(this.filename, mode, onopen)

req.size -= read
req.offset += read
function onopen (err, fd) {
if (err) return req.callback(err)
self.fd = fd
if (!self._lock(self.fd)) return req.callback(createLockError(self.filename)) // TODO: fix fd leak here
if (!self._sparse(self.fd)) return req.callback(createSparseError(self.filename))
if (!self._truncate || mode === READONLY) return req.callback(null)
fs.ftruncate(self.fd, self._size, ontruncate)
}

if (!req.size) return req.callback(null, data)
fs.read(fd, data, data.length - req.size, req.size, req.offset, onread)
function oncloseold (err) {
if (err) return onerrorafteropen(err)
self.fd = 0
fs.open(self.filename, mode, onopen)
}

function ontruncate (err) {
if (err) return onerrorafteropen(err)
req.callback(null)
}

function onerrorafteropen (err) {
fs.close(self.fd, function () {
self.fd = 0
req.callback(err)
})
}
}
}

RandomAccessFile.prototype._del = function (req) {
var fd = this.fd
_write (req) {
const data = req.data
const fd = this.fd

fs.fstat(fd, onstat)
fs.write(fd, data, 0, req.size, req.offset, onwrite)

function onstat (err, st) {
if (err) return req.callback(err)
if (req.offset + req.size < st.size) return req.callback(null)
fs.ftruncate(fd, req.offset, ontruncate)
}
function onwrite (err, wrote) {
if (err) return req.callback(err)

function ontruncate (err) {
req.callback(err)
req.size -= wrote
req.offset += wrote

if (!req.size) return req.callback(null)
fs.write(fd, data, data.length - req.size, req.size, req.offset, onwrite)
}
}
}

RandomAccessFile.prototype._stat = function (req) {
fs.fstat(this.fd, onstat)
_read (req) {
const self = this
const data = req.data || this._alloc(req.size)
const fd = this.fd

function onstat (err, st) {
req.callback(err, st)
}
}
if (!req.size) return process.nextTick(readEmpty, req)
fs.read(fd, data, 0, req.size, req.offset, onread)

RandomAccessFile.prototype._close = function (req) {
var self = this
function onread (err, read) {
if (err) return req.callback(err)
if (!read) return req.callback(createReadError(self.filename, req.offset, req.size))

fs.close(this.fd, onclose)
req.size -= read
req.offset += read

function onclose (err) {
if (err) return req.callback(err)
self.fd = 0
req.callback(null)
if (!req.size) return req.callback(null, data)
fs.read(fd, data, data.length - req.size, req.size, req.offset, onread)
}
}
}

RandomAccessFile.prototype._destroy = function (req) {
var self = this
_del (req) {
const fd = this.fd

var root = this.directory && path.resolve(path.join(this.directory, '.'))
var dir = path.resolve(path.dirname(this.filename))
fs.fstat(fd, onstat)

fs.unlink(this.filename, onunlink)
function onstat (err, st) {
if (err) return req.callback(err)
if (req.offset + req.size < st.size) return req.callback(null)
fs.ftruncate(fd, req.offset, ontruncate)
}

function onunlink (err) {
if (!self._rmdir || !root || dir === root) return req.callback(err)
fs.rmdir(dir, onrmdir)
function ontruncate (err) {
req.callback(err)
}
}

function onrmdir (err) {
dir = path.join(dir, '..')
if (err || dir === root) return req.callback(null)
fs.rmdir(dir, onrmdir)
}
}
_stat (req) {
fs.fstat(this.fd, onstat)

function open (self, mode, req) {
if (self.fd) fs.close(self.fd, oncloseold)
else fs.open(self.filename, mode, onopen)

function onopen (err, fd) {
if (err) return req.callback(err)
self.fd = fd
if (!self._lock(self.fd)) return req.callback(createLockError(self.filename)) // TODO: fix fd leak here
if (!self._sparse(self.fd)) return req.callback(createSparseError(self.filename))
if (!self._truncate || mode === READONLY) return req.callback(null)
fs.ftruncate(self.fd, self._size, ontruncate)
function onstat (err, st) {
req.callback(err, st)
}
}

function oncloseold (err) {
if (err) return onerrorafteropen(err)
self.fd = 0
fs.open(self.filename, mode, onopen)
}
_close (req) {
const self = this

function ontruncate (err) {
if (err) return onerrorafteropen(err)
req.callback(null)
}
fs.close(this.fd, onclose)

function onerrorafteropen (err) {
fs.close(self.fd, function () {
function onclose (err) {
if (err) return req.callback(err)
self.fd = 0
req.callback(err)
})
req.callback(null)
}
}

_destroy (req) {
const self = this

const root = this.directory && path.resolve(path.join(this.directory, '.'))
let dir = path.resolve(path.dirname(this.filename))

fs.unlink(this.filename, onunlink)

function onunlink (err) {
if (!self._rmdir || !root || dir === root) return req.callback(err)
fs.rmdir(dir, onrmdir)
}

function onrmdir (err) {
dir = path.join(dir, '..')
if (err || dir === root) return req.callback(null)
fs.rmdir(dir, onrmdir)
}
}
}

Expand All @@ -183,21 +179,21 @@ function noLock (fd) {
}

function createSparseError (path) {
var err = new Error('ENOTSPARSE: File could not be marked as sparse')
const err = new Error('ENOTSPARSE: File could not be marked as sparse')
err.code = 'ENOTSPARSE'
err.path = path
return err
}

function createLockError (path) {
var err = new Error('ELOCKED: File is locked')
const err = new Error('ELOCKED: File is locked')
err.code = 'ELOCKED'
err.path = path
return err
}

function createReadError (path, offset, size) {
var err = new Error('Could not satisfy length')
const err = new Error('Could not satisfy length')
err.code = 'EPARTIALREAD'
err.path = path
err.offset = offset
Expand Down
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
"description": "Continuous reading or writing to a file using random offsets and lengths",
"main": "index.js",
"scripts": {
"test": "standard && tape test.js"
"test": "standard && brittle test.js"
},
"browser": "./browser.js",
"dependencies": {
"mkdirp-classic": "^0.5.2",
"random-access-storage": "^1.1.1"
},
"devDependencies": {
"standard": "^15.0.0",
"tape": "^4.8.0"
"brittle": "^2.3.1",
"standard": "^17.0.0"
},
"repository": {
"type": "git",
Expand Down
Loading