blob.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. 'use strict'
  2. const assert = require('check-types').assert
  3. const OError = require('@overleaf/o-error')
  4. const TextOperation = require('./operation/text_operation')
  5. class NotFoundError extends OError {
  6. constructor(hash) {
  7. super(`blob ${hash} not found`, { hash })
  8. this.hash = hash
  9. }
  10. }
  11. /**
  12. * Metadata record for the content of a file.
  13. */
  14. class Blob {
  15. static HEX_HASH_RX_STRING = '^[0-9a-f]{40,40}$'
  16. static HEX_HASH_RX = new RegExp(Blob.HEX_HASH_RX_STRING)
  17. /**
  18. * Size of the largest file that we'll read to determine whether we can edit it
  19. * or not, in bytes. The final decision on whether a file is editable or not is
  20. * based on the number of characters it contains, but we need to read the file
  21. * in to determine that; so it is useful to have an upper bound on the byte
  22. * length of a file that might be editable.
  23. *
  24. * The reason for the factor of 3 is as follows. We cannot currently edit files
  25. * that contain characters outside of the basic multilingual plane, so we're
  26. * limited to characters that can be represented in a single, two-byte UCS-2
  27. * code unit. Encoding the largest such value, 0xFFFF (which is not actually
  28. * a valid character), takes three bytes in UTF-8: 0xEF 0xBF 0xBF. A file
  29. * composed entirely of three-byte UTF-8 codepoints is the worst case; in
  30. * practice, this is a very conservative upper bound.
  31. *
  32. * @type {number}
  33. */
  34. static MAX_EDITABLE_BYTE_LENGTH_BOUND = 3 * TextOperation.MAX_STRING_LENGTH
  35. static NotFoundError = NotFoundError
  36. /**
  37. * @param {string} hash
  38. * @param {number} byteLength
  39. * @param {number} [stringLength]
  40. */
  41. constructor(hash, byteLength, stringLength) {
  42. this.setHash(hash)
  43. this.setByteLength(byteLength)
  44. this.setStringLength(stringLength)
  45. }
  46. static fromRaw(raw) {
  47. if (raw) {
  48. return new Blob(raw.hash, raw.byteLength, raw.stringLength)
  49. }
  50. return null
  51. }
  52. toRaw() {
  53. return {
  54. hash: this.hash,
  55. byteLength: this.byteLength,
  56. stringLength: this.stringLength,
  57. }
  58. }
  59. /**
  60. * Hex hash.
  61. * @return {String}
  62. */
  63. getHash() {
  64. return this.hash
  65. }
  66. setHash(hash) {
  67. assert.match(hash, Blob.HEX_HASH_RX, 'bad hash')
  68. this.hash = hash
  69. }
  70. /**
  71. * Length of the blob in bytes.
  72. * @return {number}
  73. */
  74. getByteLength() {
  75. return this.byteLength
  76. }
  77. setByteLength(byteLength) {
  78. assert.integer(byteLength, 'bad byteLength')
  79. this.byteLength = byteLength
  80. }
  81. /**
  82. * Utf-8 length of the blob content, if it appears to be valid UTF-8.
  83. * @return {number|undefined}
  84. */
  85. getStringLength() {
  86. return this.stringLength
  87. }
  88. setStringLength(stringLength) {
  89. assert.maybe.integer(stringLength, 'bad stringLength')
  90. this.stringLength = stringLength
  91. }
  92. }
  93. module.exports = Blob