Source: main/webapp/modules/StringWriter.js

  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. var Guacamole = Guacamole || {};
  20. /**
  21. * A writer which automatically writes to the given output stream with text
  22. * data.
  23. *
  24. * @constructor
  25. * @param {!Guacamole.OutputStream} stream
  26. * The stream that data will be written to.
  27. */
  28. Guacamole.StringWriter = function(stream) {
  29. /**
  30. * Reference to this Guacamole.StringWriter.
  31. *
  32. * @private
  33. * @type {!Guacamole.StringWriter}
  34. */
  35. var guac_writer = this;
  36. /**
  37. * Wrapped Guacamole.ArrayBufferWriter.
  38. *
  39. * @private
  40. * @type {!Guacamole.ArrayBufferWriter}
  41. */
  42. var array_writer = new Guacamole.ArrayBufferWriter(stream);
  43. /**
  44. * Internal buffer for UTF-8 output.
  45. *
  46. * @private
  47. * @type {!Uint8Array}
  48. */
  49. var buffer = new Uint8Array(8192);
  50. /**
  51. * The number of bytes currently in the buffer.
  52. *
  53. * @private
  54. * @type {!number}
  55. */
  56. var length = 0;
  57. // Simply call onack for acknowledgements
  58. array_writer.onack = function(status) {
  59. if (guac_writer.onack)
  60. guac_writer.onack(status);
  61. };
  62. /**
  63. * Expands the size of the underlying buffer by the given number of bytes,
  64. * updating the length appropriately.
  65. *
  66. * @private
  67. * @param {!number} bytes
  68. * The number of bytes to add to the underlying buffer.
  69. */
  70. function __expand(bytes) {
  71. // Resize buffer if more space needed
  72. if (length+bytes >= buffer.length) {
  73. var new_buffer = new Uint8Array((length+bytes)*2);
  74. new_buffer.set(buffer);
  75. buffer = new_buffer;
  76. }
  77. length += bytes;
  78. }
  79. /**
  80. * Appends a single Unicode character to the current buffer, resizing the
  81. * buffer if necessary. The character will be encoded as UTF-8.
  82. *
  83. * @private
  84. * @param {!number} codepoint
  85. * The codepoint of the Unicode character to append.
  86. */
  87. function __append_utf8(codepoint) {
  88. var mask;
  89. var bytes;
  90. // 1 byte
  91. if (codepoint <= 0x7F) {
  92. mask = 0x00;
  93. bytes = 1;
  94. }
  95. // 2 byte
  96. else if (codepoint <= 0x7FF) {
  97. mask = 0xC0;
  98. bytes = 2;
  99. }
  100. // 3 byte
  101. else if (codepoint <= 0xFFFF) {
  102. mask = 0xE0;
  103. bytes = 3;
  104. }
  105. // 4 byte
  106. else if (codepoint <= 0x1FFFFF) {
  107. mask = 0xF0;
  108. bytes = 4;
  109. }
  110. // If invalid codepoint, append replacement character
  111. else {
  112. __append_utf8(0xFFFD);
  113. return;
  114. }
  115. // Offset buffer by size
  116. __expand(bytes);
  117. var offset = length - 1;
  118. // Add trailing bytes, if any
  119. for (var i=1; i<bytes; i++) {
  120. buffer[offset--] = 0x80 | (codepoint & 0x3F);
  121. codepoint >>= 6;
  122. }
  123. // Set initial byte
  124. buffer[offset] = mask | codepoint;
  125. }
  126. /**
  127. * Encodes the given string as UTF-8, returning an ArrayBuffer containing
  128. * the resulting bytes.
  129. *
  130. * @private
  131. * @param {!string} text
  132. * The string to encode as UTF-8.
  133. *
  134. * @return {!Uint8Array}
  135. * The encoded UTF-8 data.
  136. */
  137. function __encode_utf8(text) {
  138. // Fill buffer with UTF-8
  139. for (var i=0; i<text.length; i++) {
  140. var codepoint = text.charCodeAt(i);
  141. __append_utf8(codepoint);
  142. }
  143. // Flush buffer
  144. if (length > 0) {
  145. var out_buffer = buffer.subarray(0, length);
  146. length = 0;
  147. return out_buffer;
  148. }
  149. }
  150. /**
  151. * Sends the given text.
  152. *
  153. * @param {!string} text
  154. * The text to send.
  155. */
  156. this.sendText = function(text) {
  157. if (text.length)
  158. array_writer.sendData(__encode_utf8(text));
  159. };
  160. /**
  161. * Signals that no further text will be sent, effectively closing the
  162. * stream.
  163. */
  164. this.sendEnd = function() {
  165. array_writer.sendEnd();
  166. };
  167. /**
  168. * Fired for received data, if acknowledged by the server.
  169. *
  170. * @event
  171. * @param {!Guacamole.Status} status
  172. * The status of the operation.
  173. */
  174. this.onack = null;
  175. };