var fs = require('fs')
|
var bufferEqual = require('buffer-equal')
|
var FileDescriptorQueue = require('../fs/FileDescriptorQueue')
|
var closeFilesSync = require('./closeFile').closeFilesSync
|
var closeFilesAsync = require('./closeFile').closeFilesAsync
|
var fsPromise = require('../fs/fsPromise')
|
var BufferPool = require('../fs/BufferPool')
|
|
var MAX_CONCURRENT_FILE_COMPARE = 8
|
var BUF_SIZE = 100000
|
var fdQueue = new FileDescriptorQueue(MAX_CONCURRENT_FILE_COMPARE * 2)
|
var bufferPool = new BufferPool(BUF_SIZE, MAX_CONCURRENT_FILE_COMPARE); // fdQueue guarantees there will be no more than MAX_CONCURRENT_FILE_COMPARE async processes accessing the buffers concurrently
|
|
|
/**
|
* Compares two partial buffers.
|
*/
|
var compareBuffers = function (buf1, buf2, contentSize) {
|
return bufferEqual(buf1.slice(0, contentSize), buf2.slice(0, contentSize))
|
}
|
|
/**
|
* Compares two files by content.
|
*/
|
var compareSync = function (path1, stat1, path2, stat2, options) {
|
var fd1, fd2
|
if (stat1.size !== stat2.size) {
|
return false
|
}
|
var bufferPair = bufferPool.allocateBuffers()
|
try {
|
fd1 = fs.openSync(path1, 'r')
|
fd2 = fs.openSync(path2, 'r')
|
var buf1 = bufferPair.buf1
|
var buf2 = bufferPair.buf2
|
var progress = 0
|
while (true) {
|
var size1 = fs.readSync(fd1, buf1, 0, BUF_SIZE, null)
|
var size2 = fs.readSync(fd2, buf2, 0, BUF_SIZE, null)
|
if (size1 !== size2) {
|
return false
|
} else if (size1 === 0) {
|
// End of file reached
|
return true
|
} else if (!compareBuffers(buf1, buf2, size1)) {
|
return false
|
}
|
}
|
} finally {
|
closeFilesSync(fd1, fd2)
|
bufferPool.freeBuffers(bufferPair)
|
}
|
}
|
|
|
/**
|
* Compares two files by content
|
*/
|
var compareAsync = function (path1, stat1, path2, stat2, options) {
|
var fd1, fd2
|
var bufferPair
|
if (stat1.size !== stat2.size) {
|
return Promise.resolve(false)
|
}
|
return Promise.all([fdQueue.promises.open(path1, 'r'), fdQueue.promises.open(path2, 'r')])
|
.then(function (fds) {
|
bufferPair = bufferPool.allocateBuffers()
|
fd1 = fds[0]
|
fd2 = fds[1]
|
var buf1 = bufferPair.buf1
|
var buf2 = bufferPair.buf2
|
var progress = 0
|
var compareAsyncInternal = function () {
|
return Promise.all([
|
fsPromise.read(fd1, buf1, 0, BUF_SIZE, null),
|
fsPromise.read(fd2, buf2, 0, BUF_SIZE, null)
|
]).then(function (bufferSizes) {
|
var size1 = bufferSizes[0]
|
var size2 = bufferSizes[1]
|
if (size1 !== size2) {
|
return false
|
} else if (size1 === 0) {
|
// End of file reached
|
return true
|
} else if (!compareBuffers(buf1, buf2, size1)) {
|
return false
|
} else {
|
return compareAsyncInternal()
|
}
|
})
|
}
|
return compareAsyncInternal()
|
})
|
.then(
|
// 'finally' polyfill for node 8 and below
|
function (res) {
|
return finalizeAsync(fd1, fd2, bufferPair).then(() => res)
|
},
|
function (err) {
|
return finalizeAsync(fd1, fd2, bufferPair).then(() => { throw err; })
|
}
|
)
|
}
|
|
function finalizeAsync(fd1, fd2, bufferPair) {
|
bufferPool.freeBuffers(bufferPair)
|
return closeFilesAsync(fd1, fd2, fdQueue)
|
}
|
|
module.exports = {
|
compareSync: compareSync,
|
compareAsync: compareAsync
|
}
|