blob.js 2.7 KB

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