blob.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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. constructor(hash, byteLength, stringLength) {
  37. this.setHash(hash)
  38. this.setByteLength(byteLength)
  39. this.setStringLength(stringLength)
  40. }
  41. static fromRaw(raw) {
  42. if (raw) {
  43. return new Blob(raw.hash, raw.byteLength, raw.stringLength)
  44. }
  45. return null
  46. }
  47. toRaw() {
  48. return {
  49. hash: this.hash,
  50. byteLength: this.byteLength,
  51. stringLength: this.stringLength,
  52. }
  53. }
  54. /**
  55. * Hex hash.
  56. * @return {?String}
  57. */
  58. getHash() {
  59. return this.hash
  60. }
  61. setHash(hash) {
  62. assert.maybe.match(hash, Blob.HEX_HASH_RX, 'bad hash')
  63. this.hash = hash
  64. }
  65. /**
  66. * Length of the blob in bytes.
  67. * @return {number}
  68. */
  69. getByteLength() {
  70. return this.byteLength
  71. }
  72. setByteLength(byteLength) {
  73. assert.maybe.integer(byteLength, 'bad byteLength')
  74. this.byteLength = byteLength
  75. }
  76. /**
  77. * Utf-8 length of the blob content, if it appears to be valid UTF-8.
  78. * @return {?number}
  79. */
  80. getStringLength() {
  81. return this.stringLength
  82. }
  83. setStringLength(stringLength) {
  84. assert.maybe.integer(stringLength, 'bad stringLength')
  85. this.stringLength = stringLength
  86. }
  87. }
  88. module.exports = Blob