file-handle.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author David Sehnal <david.sehnal@gmail.com>
  6. */
  7. import { defaults, noop } from 'mol-util';
  8. import { SimpleBuffer } from './simple-buffer';
  9. // only import 'fs' in node.js
  10. const fs = typeof document === 'undefined' ? require('fs') as typeof import('fs') : void 0;
  11. export interface FileHandle {
  12. /**
  13. * Asynchronously reads data, returning buffer and number of bytes read
  14. *
  15. * @param position The offset from the beginning of the file from which data should be read.
  16. * @param sizeOrBuffer The buffer the data will be read from.
  17. * @param length The number of bytes to read.
  18. * @param byteOffset The offset in the buffer at which to start writing.
  19. */
  20. readBuffer(position: number, sizeOrBuffer: SimpleBuffer | number, length?: number, byteOffset?: number): Promise<{ bytesRead: number, buffer: SimpleBuffer }>
  21. /**
  22. * Asynchronously writes buffer, returning the number of bytes written.
  23. *
  24. * @param position — The offset from the beginning of the file where this data should be written.
  25. * @param buffer - The buffer data to be written.
  26. * @param length — The number of bytes to write. If not supplied, defaults to buffer.length
  27. */
  28. writeBuffer(position: number, buffer: SimpleBuffer, length?: number): Promise<number>
  29. /**
  30. * Synchronously writes buffer, returning the number of bytes written.
  31. *
  32. * @param position — The offset from the beginning of the file where this data should be written.
  33. * @param buffer - The buffer data to be written.
  34. * @param length — The number of bytes to write. If not supplied, defaults to buffer.length
  35. */
  36. writeBufferSync(position: number, buffer: SimpleBuffer, length?: number): number
  37. /** Closes a file handle */
  38. close(): void
  39. }
  40. export namespace FileHandle {
  41. export function fromBuffer(buffer: SimpleBuffer): FileHandle {
  42. return {
  43. readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, size?: number, byteOffset?: number) => {
  44. let bytesRead: number
  45. let outBuffer: SimpleBuffer
  46. if (typeof sizeOrBuffer === 'number') {
  47. size = defaults(size, sizeOrBuffer)
  48. const start = position
  49. const end = Math.min(buffer.length, start + size)
  50. bytesRead = end - start
  51. outBuffer = SimpleBuffer.fromUint8Array(new Uint8Array(buffer.buffer, start, end - start))
  52. } else {
  53. size = defaults(size, sizeOrBuffer.length)
  54. const start = position
  55. const end = Math.min(buffer.length, start + size)
  56. sizeOrBuffer.set(buffer.subarray(start, end), byteOffset)
  57. bytesRead = end - start
  58. outBuffer = sizeOrBuffer
  59. }
  60. if (size !== bytesRead) {
  61. console.warn(`byteCount ${size} and bytesRead ${bytesRead} differ`)
  62. }
  63. return Promise.resolve({ bytesRead, buffer: outBuffer })
  64. },
  65. writeBuffer: (position: number, buffer: SimpleBuffer, length?: number) => {
  66. length = defaults(length, buffer.length)
  67. console.error('.writeBuffer not implemented for FileHandle.fromBuffer')
  68. return Promise.resolve(0)
  69. },
  70. writeBufferSync: (position: number, buffer: SimpleBuffer, length?: number, ) => {
  71. length = defaults(length, buffer.length)
  72. console.error('.writeSync not implemented for FileHandle.fromBuffer')
  73. return 0
  74. },
  75. close: noop
  76. }
  77. }
  78. export function fromDescriptor(file: number): FileHandle {
  79. if (fs === undefined) throw new Error('fs module not available')
  80. return {
  81. readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, length?: number, byteOffset?: number) => {
  82. return new Promise((res, rej) => {
  83. let outBuffer: SimpleBuffer
  84. if (typeof sizeOrBuffer === 'number') {
  85. byteOffset = defaults(byteOffset, 0)
  86. length = defaults(length, sizeOrBuffer)
  87. outBuffer = SimpleBuffer.fromArrayBuffer(new ArrayBuffer(sizeOrBuffer));
  88. } else {
  89. byteOffset = defaults(byteOffset, 0)
  90. length = defaults(length, sizeOrBuffer.length)
  91. outBuffer = sizeOrBuffer
  92. }
  93. fs.read(file, outBuffer, byteOffset, length, position, (err, bytesRead, buffer) => {
  94. if (err) {
  95. rej(err);
  96. return;
  97. }
  98. if (length !== bytesRead) {
  99. console.warn(`byteCount ${length} and bytesRead ${bytesRead} differ`)
  100. }
  101. res({ bytesRead, buffer });
  102. });
  103. })
  104. },
  105. writeBuffer: (position: number, buffer: SimpleBuffer, length?: number) => {
  106. length = defaults(length, buffer.length)
  107. return new Promise<number>((res, rej) => {
  108. fs.write(file, buffer, 0, length, position, (err, written) => {
  109. if (err) rej(err);
  110. else res(written);
  111. })
  112. })
  113. },
  114. writeBufferSync: (position: number, buffer: Uint8Array, length?: number) => {
  115. length = defaults(length, buffer.length)
  116. return fs.writeSync(file, buffer, 0, length, position);
  117. },
  118. close: () => {
  119. try {
  120. if (file !== void 0) fs.close(file, noop);
  121. } catch (e) {
  122. }
  123. }
  124. }
  125. }
  126. }