| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library analyzer.src.summary.api_signature; |
| 6 |
| 7 import 'dart:convert'; |
| 8 import 'dart:typed_data'; |
| 9 |
| 10 import 'package:convert/convert.dart'; |
| 11 import 'package:crypto/crypto.dart'; |
| 12 |
| 13 /** |
| 14 * An instance of [ApiSignature] collects data in the form of primitive types |
| 15 * (strings, ints, bools, etc.) from a summary "builder" object, and uses them |
| 16 * to generate an MD5 signature of a the non-informative parts of the summary |
| 17 * (i.e. those parts representing the API of the code being summarized). |
| 18 * |
| 19 * Note that the data passed to the MD5 signature algorithm is untyped. So, for |
| 20 * instance, an API signature built from a sequence of `false` booleans is |
| 21 * likely to match an API signature built from a sequence of zeros. The caller |
| 22 * should take this into account; e.g. if a data structure may be represented |
| 23 * either by a boolean or an int, the caller should encode a tag distinguishing |
| 24 * the two representations before encoding the data. |
| 25 */ |
| 26 class ApiSignature { |
| 27 /** |
| 28 * Version number of the code in this class. Any time this class is changed |
| 29 * in a way that affects the data collected in [_data], this version number |
| 30 * should be incremented, so that a summary signed by a newer version of the |
| 31 * signature algorithm won't accidentally have the same signature as a summary |
| 32 * signed by an older version. |
| 33 */ |
| 34 static const int _VERSION = 0; |
| 35 |
| 36 /** |
| 37 * Data accumulated so far. |
| 38 */ |
| 39 ByteData _data = new ByteData(4096); |
| 40 |
| 41 /** |
| 42 * Offset into [_data] where the next byte should be written. |
| 43 */ |
| 44 int _offset = 0; |
| 45 |
| 46 /** |
| 47 * Create an [ApiSignature] which is ready to accept data. |
| 48 */ |
| 49 ApiSignature() { |
| 50 addInt(_VERSION); |
| 51 } |
| 52 |
| 53 /** |
| 54 * For testing only: create an [ApiSignature] which doesn't include any |
| 55 * version information. This makes it easier to unit tests, since the data |
| 56 * is stable even if [_VERSION] is changed. |
| 57 */ |
| 58 ApiSignature.unversioned(); |
| 59 |
| 60 /** |
| 61 * Collect a boolean value. |
| 62 */ |
| 63 void addBool(bool b) { |
| 64 _makeRoom(1); |
| 65 _data.setUint8(_offset, b ? 1 : 0); |
| 66 _offset++; |
| 67 } |
| 68 |
| 69 /** |
| 70 * Collect a sequence of arbitrary bytes. Note that the length is not |
| 71 * collected, so for example `addBytes([1, 2]);` will have the same effect as |
| 72 * `addBytes([1]); addBytes([2]);`. |
| 73 */ |
| 74 void addBytes(List<int> bytes) { |
| 75 _makeRoom(bytes.length); |
| 76 new Uint8List.view(_data.buffer) |
| 77 .setRange(_offset, _offset + bytes.length, bytes); |
| 78 _offset += bytes.length; |
| 79 } |
| 80 |
| 81 /** |
| 82 * Collect a double-precision floating point value. |
| 83 */ |
| 84 void addDouble(double d) { |
| 85 _makeRoom(8); |
| 86 _data.setFloat64(_offset, d, Endianness.LITTLE_ENDIAN); |
| 87 _offset += 8; |
| 88 } |
| 89 |
| 90 /** |
| 91 * Collect a 32-bit unsigned integer value. |
| 92 */ |
| 93 void addInt(int i) { |
| 94 _makeRoom(4); |
| 95 _data.setUint32(_offset, i, Endianness.LITTLE_ENDIAN); |
| 96 _offset += 4; |
| 97 } |
| 98 |
| 99 /** |
| 100 * Collect a string. |
| 101 */ |
| 102 void addString(String s) { |
| 103 List<int> bytes = UTF8.encode(s); |
| 104 addInt(bytes.length); |
| 105 addBytes(bytes); |
| 106 } |
| 107 |
| 108 /** |
| 109 * For testing only: retrieve the internal representation of the data that |
| 110 * has been collected. |
| 111 */ |
| 112 List<int> getBytes_forDebug() { |
| 113 return new Uint8List.view(_data.buffer, 0, _offset).toList(); |
| 114 } |
| 115 |
| 116 /** |
| 117 * Return a hex-encoded MD5 signature of the data collected so far. |
| 118 */ |
| 119 String toHex() { |
| 120 return hex.encode( |
| 121 md5.convert(new Uint8List.view(_data.buffer, 0, _offset)).bytes); |
| 122 } |
| 123 |
| 124 /** |
| 125 * Ensure that [spaceNeeded] bytes can be added to [_data] at [_offset] |
| 126 * (copying it to a larger object if necessary). |
| 127 */ |
| 128 void _makeRoom(int spaceNeeded) { |
| 129 int oldLength = _data.lengthInBytes; |
| 130 if (_offset + spaceNeeded > oldLength) { |
| 131 int newLength = 2 * (_offset + spaceNeeded); |
| 132 ByteData newData = new ByteData(newLength); |
| 133 new Uint8List.view(newData.buffer) |
| 134 .setRange(0, oldLength, new Uint8List.view(_data.buffer)); |
| 135 _data = newData; |
| 136 } |
| 137 } |
| 138 } |
| OLD | NEW |