Index: sdk/lib/vmservice/asset.dart |
diff --git a/sdk/lib/vmservice/asset.dart b/sdk/lib/vmservice/asset.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7835e74442397d14d788f69124d2682246841b94 |
--- /dev/null |
+++ b/sdk/lib/vmservice/asset.dart |
@@ -0,0 +1,205 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+part of dart._vmservice; |
+ |
+class Asset { |
+ final String name; |
+ final Uint8List data; |
+ |
+ Asset(this.name, this.data); |
+ |
+ String get mimeType { |
+ var extensionStart = name.lastIndexOf('.'); |
+ var extension = name.substring(extensionStart+1); |
+ switch (extension) { |
+ case 'html': |
+ return 'text/html; charset=UTF-8'; |
+ case 'dart': |
+ return 'application/dart; charset=UTF-8'; |
+ case 'js': |
+ return 'application/javascript; charset=UTF-8'; |
+ case 'css': |
+ return 'text/css; charset=UTF-8'; |
+ case 'gif': |
+ return 'image/gif'; |
+ case 'png': |
+ return 'image/png'; |
+ case 'jpg': |
+ return 'image/jpeg'; |
+ case 'jpeg': |
+ return 'image/jpeg'; |
+ case 'svg': |
+ return 'image/svg+xml'; |
+ default: |
+ return 'text/plain'; |
+ } |
+ } |
+ |
+ /// Call to request assets from the embedder. |
+ static Map<String, Asset> request() { |
+ Map<String, Asset> assets = new Map<String, Asset>(); |
+ Uint8List tarBytes = _requestAssets(); |
+ if (tarBytes == null) { |
+ return assets; |
+ } |
+ _TarArchive archive = new _TarArchive(tarBytes); |
+ while (archive.hasNext()) { |
+ Asset asset = archive.next(); |
+ if (asset == null) { |
+ // Skip over special files. |
+ continue; |
+ } |
+ assets[asset.name] = asset; |
+ } |
+ return assets; |
+ } |
+ |
+ String toString() => '$name ($mimeType)'; |
+} |
+ |
+ |
+class _ByteStream { |
+ final Uint8List bytes; |
+ final int offset; |
+ int get length => bytes.length - offset; |
+ int _cursor = 0; |
+ |
+ _ByteStream(this.bytes, [this.offset = 0]); |
+ |
+ void reset() { |
+ _cursor = 0; |
+ } |
+ |
+ int peekByte([int index = 0]) => bytes[offset + _cursor + index]; |
+ |
+ int readByte() { |
+ int r = peekByte(); |
+ _advance(1); |
+ return r; |
+ } |
+ |
+ void skip(int bytes) => _advance(bytes); |
+ |
+ void seekToNextBlock(int blockSize) { |
+ int remainder = blockSize - (_cursor % blockSize); |
+ _advance(remainder); |
+ } |
+ |
+ void _advance(int bytes) { |
+ _cursor += bytes; |
+ if (_cursor > length) { |
+ _cursor = length; |
+ } |
+ } |
+ |
+ int get remaining => length - _cursor; |
+ bool get hasMore => remaining > 0; |
+ int get cursor => _cursor; |
+ void set cursor(int cursor) { |
+ _cursor = cursor; |
+ if (_cursor > length) { |
+ _cursor = length; |
+ } |
+ } |
+} |
+ |
+class _TarArchive { |
+ static const List<int> tarMagic = const [ 0x75, 0x73, 0x74, 0x61, 0x72, 0 ]; |
+ static const List<int> tarVersion = const [ 0x30, 0x30 ]; |
+ static const int tarHeaderSize = 512; |
+ static const int tarHeaderFilenameSize = 100; |
+ static const int tarHeaderFilenameOffset = 0; |
+ static const int tarHeaderSizeSize = 12; |
+ static const int tarHeaderSizeOffset = 124; |
+ static const int tarHeaderTypeSize = 1; |
+ static const int tarHeaderTypeOffset = 156; |
+ static const int tarHeaderFileType = 0x30; |
+ |
+ static String _readCString(_ByteStream bs, int length) { |
+ StringBuffer sb = new StringBuffer(); |
+ int count = 0; |
+ while (bs.hasMore && count < length) { |
+ if (bs.peekByte() == 0) { |
+ // Null character. |
+ break; |
+ } |
+ sb.writeCharCode(bs.readByte()); |
+ count++; |
+ } |
+ return sb.toString(); |
+ } |
+ |
+ static String _readFilename(_ByteStream bs) { |
+ String filename = _readCString(bs, tarHeaderFilenameSize); |
+ if (filename.startsWith('/')) { |
+ return filename; |
+ } |
+ return '/' + filename; |
+ } |
+ |
+ static Uint8List _readContents(_ByteStream bs, int size) { |
+ Uint8List result = new Uint8List(size); |
+ int i = 0; |
+ while (bs.hasMore && i < size) { |
+ result[i] = bs.readByte(); |
+ i++; |
+ } |
+ bs.seekToNextBlock(tarHeaderSize); |
+ return result; |
+ } |
+ |
+ static void _skipContents(_ByteStream bs, int size) { |
+ bs.skip(size); |
+ bs.seekToNextBlock(tarHeaderSize); |
+ } |
+ |
+ static int _readSize(_ByteStream bs) { |
+ String octalSize = _readCString(bs, tarHeaderSizeSize); |
+ return int.parse(octalSize, |
+ radix: 8, |
+ onError: (_) => 0); |
+ } |
+ |
+ static int _readType(_ByteStream bs) { |
+ return bs.readByte(); |
+ } |
+ |
+ static bool _endOfArchive(_ByteStream bs) { |
+ if (bs.remaining < (tarHeaderSize * 2)) { |
+ return true; |
+ } |
+ for (int i = 0; i < (tarHeaderSize * 2); i++) { |
+ if (bs.peekByte(i) != 0) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ final _ByteStream _bs; |
+ |
+ _TarArchive(Uint8List bytes) |
+ : _bs = new _ByteStream(bytes); |
+ |
+ bool hasNext() { |
+ return !_endOfArchive(_bs); |
+ } |
+ |
+ Asset next() { |
+ int startOfBlock = _bs.cursor; |
+ String filename = _readFilename(_bs); |
+ _bs.cursor = startOfBlock + tarHeaderSizeOffset; |
+ int size = _readSize(_bs); |
+ _bs.cursor = startOfBlock + tarHeaderTypeOffset; |
+ int type = _readType(_bs); |
+ _bs.seekToNextBlock(tarHeaderSize); |
+ if (type != tarHeaderFileType) { |
+ _skipContents(_bs, size); |
+ return null; |
+ } |
+ Uint8List bytes = _readContents(_bs, size); |
+ return new Asset(filename, bytes); |
+ } |
+} |