|  | // Copyright (c) 2020, 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. | 
|  | // | 
|  | // Benchmark for UTF-8 decoding | 
|  |  | 
|  | import 'dart:convert'; | 
|  | import 'dart:typed_data'; | 
|  |  | 
|  | import 'package:benchmark_harness/benchmark_harness.dart'; | 
|  |  | 
|  | import 'datext_latin1_10k.dart'; | 
|  | import 'entext_ascii_10k.dart'; | 
|  | import 'netext_3_10k.dart'; | 
|  | import 'rutext_2_10k.dart'; | 
|  | import 'sktext_10k.dart'; | 
|  | import 'zhtext_10k.dart'; | 
|  |  | 
|  | class Utf8Decode extends BenchmarkBase { | 
|  | final String language; | 
|  | final String text; | 
|  | final int size; | 
|  | final bool allowMalformed; | 
|  | late List<Uint8List> chunks; | 
|  | int totalInputSize = 0; | 
|  | int totalOutputSize = 0; | 
|  |  | 
|  | static String _makeName(String language, int size, bool allowMalformed) { | 
|  | String name = 'Utf8Decode.$language.'; | 
|  | name += size >= 1000000 | 
|  | ? '${size ~/ 1000000}M' | 
|  | : size >= 1000 ? '${size ~/ 1000}k' : '$size'; | 
|  | if (allowMalformed) name += '.malformed'; | 
|  | return name; | 
|  | } | 
|  |  | 
|  | Utf8Decode(this.language, this.text, this.size, this.allowMalformed) | 
|  | : super(_makeName(language, size, allowMalformed)); | 
|  |  | 
|  | @override | 
|  | void setup() { | 
|  | final Uint8List data = utf8.encode(text) as Uint8List; | 
|  | if (data.length != 10000) { | 
|  | throw 'Expected input data of exactly 10000 bytes.'; | 
|  | } | 
|  | if (size < data.length) { | 
|  | // Split into chunks. | 
|  | chunks = <Uint8List>[]; | 
|  | int startPos = 0; | 
|  | for (int pos = size; pos < data.length; pos += size) { | 
|  | int endPos = pos; | 
|  | while ((data[endPos] & 0xc0) == 0x80) { | 
|  | endPos--; | 
|  | } | 
|  | chunks.add(Uint8List.fromList(data.sublist(startPos, endPos))); | 
|  | startPos = endPos; | 
|  | } | 
|  | chunks.add(Uint8List.fromList(data.sublist(startPos, data.length))); | 
|  | totalInputSize = data.length; | 
|  | totalOutputSize = text.length; | 
|  | } else if (size > data.length) { | 
|  | // Repeat data to the desired length. | 
|  | final Uint8List expanded = Uint8List(size); | 
|  | for (int i = 0; i < size; i++) { | 
|  | expanded[i] = data[i % data.length]; | 
|  | } | 
|  | chunks = <Uint8List>[expanded]; | 
|  | totalInputSize = size; | 
|  | totalOutputSize = text.length * size ~/ data.length; | 
|  | } else { | 
|  | // Use data as is. | 
|  | chunks = <Uint8List>[data]; | 
|  | totalInputSize = data.length; | 
|  | totalOutputSize = text.length; | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void run() { | 
|  | int lengthSum = 0; | 
|  | for (int i = 0; i < chunks.length; i++) { | 
|  | final String s = utf8.decode(chunks[i], allowMalformed: allowMalformed); | 
|  | lengthSum += s.length; | 
|  | } | 
|  | if (lengthSum != totalOutputSize) { | 
|  | throw 'Output length doesn\'t match expected.'; | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void exercise() { | 
|  | // Only a single run per measurement. | 
|  | run(); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void warmup() { | 
|  | BenchmarkBase.measureFor(run, 1000); | 
|  | } | 
|  |  | 
|  | @override | 
|  | double measure() { | 
|  | // Report time per input byte. | 
|  | return super.measure() / totalInputSize; | 
|  | } | 
|  |  | 
|  | @override | 
|  | void report() { | 
|  | // Report time in nanoseconds. | 
|  | final double score = measure() * 1000.0; | 
|  | print('$name(RunTime): $score ns.'); | 
|  | } | 
|  | } | 
|  |  | 
|  | void main(List<String> args) { | 
|  | const texts = { | 
|  | 'en': en, | 
|  | 'da': da, | 
|  | 'sk': sk, | 
|  | 'ru': ru, | 
|  | 'ne': ne, | 
|  | 'zh': zh, | 
|  | }; | 
|  | final bool testMalformed = args.isNotEmpty && args.first == 'malformed'; | 
|  | final benchmarks = [ | 
|  | // Only benchmark with allowMalformed: false unless specified otherwise. | 
|  | for (bool allowMalformed in [false, if (testMalformed) true]) | 
|  | for (int size in [10, 10000, 10000000]) | 
|  | for (String language in texts.keys) | 
|  | () => Utf8Decode(language, texts[language]!, size, allowMalformed) | 
|  | ]; | 
|  |  | 
|  | for (var bm in benchmarks) { | 
|  | bm().report(); | 
|  | } | 
|  | } |