blob.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. 'use strict'
  2. const assert = require('check-types').assert
  3. const OError = require('@overleaf/o-error')
  4. class NotFoundError extends OError {
  5. constructor(hash) {
  6. super(`blob ${hash} not found`, { hash })
  7. this.hash = hash
  8. }
  9. }
  10. /**
  11. * Metadata record for the content of a file.
  12. */
  13. class Blob {
  14. static HEX_HASH_RX_STRING = '^[0-9a-f]{40,40}$'
  15. static HEX_HASH_RX = new RegExp(Blob.HEX_HASH_RX_STRING)
  16. /**
  17. * Size of the largest file that we'll read to determine whether we can edit it
  18. * or not, in bytes. The final decision on whether a file is editable or not is
  19. * based on the number of characters it contains, but we need to read the file
  20. * in to determine that; so it is useful to have an upper bound on the byte
  21. * length of a file that might be editable.
  22. *
  23. * This used to be 3 times the max editable file length to account for 3-byte
  24. * UTF-8 codepoints. However, editable file blobs now include tracked deletes
  25. * and the system used to allow unlimited tracked deletes on a single file.
  26. * A practical limit is the 16 MB Mongo size limit. It wouldn't have been
  27. * possible to store more than 16 MB of tracked deletes. We therefore fall
  28. * back to this limit.
  29. */
  30. static MAX_EDITABLE_BYTE_LENGTH_BOUND = 16 * 1024 * 1024
  31. static NotFoundError = NotFoundError
  32. constructor(hash, byteLength, stringLength) {
  33. this.setHash(hash)
  34. this.setByteLength(byteLength)
  35. this.setStringLength(stringLength)
  36. }
  37. static fromRaw(raw) {
  38. if (raw) {
  39. return new Blob(raw.hash, raw.byteLength, raw.stringLength)
  40. }
  41. return null
  42. }
  43. toRaw() {
  44. return {
  45. hash: this.hash,
  46. byteLength: this.byteLength,
  47. stringLength: this.stringLength,
  48. }
  49. }
  50. /**
  51. * Hex hash.
  52. * @return {?String}
  53. */
  54. getHash() {
  55. return this.hash
  56. }
  57. setHash(hash) {
  58. assert.maybe.match(hash, Blob.HEX_HASH_RX, 'bad hash')
  59. this.hash = hash
  60. }
  61. /**
  62. * Length of the blob in bytes.
  63. * @return {number}
  64. */
  65. getByteLength() {
  66. return this.byteLength
  67. }
  68. setByteLength(byteLength) {
  69. assert.maybe.integer(byteLength, 'bad byteLength')
  70. this.byteLength = byteLength
  71. }
  72. /**
  73. * Utf-8 length of the blob content, if it appears to be valid UTF-8.
  74. * @return {?number}
  75. */
  76. getStringLength() {
  77. return this.stringLength
  78. }
  79. setStringLength(stringLength) {
  80. assert.maybe.integer(stringLength, 'bad stringLength')
  81. this.stringLength = stringLength
  82. }
  83. }
  84. module.exports = Blob