| 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);
|
| + }
|
| +}
|
|
|