├── example ├── android │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── app │ │ ├── src │ │ │ └── main │ │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.java │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── .gitignore │ ├── .idea │ │ ├── modules.xml │ │ ├── misc.xml │ │ ├── gradle.xml │ │ ├── runConfigurations.xml │ │ └── codeStyles │ │ │ └── Project.xml │ ├── settings.gradle │ ├── build.gradle │ ├── gradlew.bat │ └── gradlew ├── ios │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ ├── flutter_export_environment.sh │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── AppDelegate.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── main.m │ │ ├── AppDelegate.m │ │ ├── Info.plist │ │ └── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ └── project.pbxproj │ └── .gitignore ├── .gitignore ├── README.md ├── .idea │ ├── libraries │ │ ├── Flutter_Plugins.xml │ │ ├── Flutter_for_Android.xml │ │ ├── Dart_SDK.xml │ │ └── Dart_Packages.xml │ ├── runConfigurations │ │ └── main_dart.xml │ ├── modules.xml │ ├── misc.xml │ ├── codeStyles │ │ └── Project.xml │ └── workspace.xml ├── .metadata ├── pubspec.yaml ├── example.iml ├── test │ └── widget_test.dart ├── example_android.iml ├── lib │ ├── main.dart │ └── test_disk_cache.dart └── pubspec.lock ├── lib ├── disk_lru_cache.dart └── _src │ ├── ioutil.dart │ ├── lru_map.dart │ └── disk_lru_cache.dart ├── .idea ├── encodings.xml ├── vcs.xml ├── libraries │ ├── Flutter_Plugins.xml │ ├── Dart_SDK.xml │ └── Dart_Packages.xml ├── modules.xml └── misc.xml ├── .gitignore ├── .travis.yml ├── dev └── bots │ ├── travis_script.sh │ └── travis_install.sh ├── CHANGELOG.md ├── pubspec.yaml ├── LICENSE ├── disk_lru_cache.iml ├── README.md ├── test ├── lru_map_test.dart └── disk_lru_cache_test.dart ├── flutter_01.log └── pubspec.lock /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | .flutter-plugins 10 | -------------------------------------------------------------------------------- /lib/disk_lru_cache.dart: -------------------------------------------------------------------------------- 1 | library disk_lru_cache; 2 | 3 | export '_src/disk_lru_cache.dart'; 4 | export '_src/ioutil.dart'; 5 | export '_src/lru_map.dart'; 6 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online 8 | [documentation](https://flutter.io/). 9 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | GeneratedPluginRegistrant.java 11 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | ios/.generated/ 9 | ios/Flutter/Generated.xcconfig 10 | ios/Runner/GeneratedPluginRegistrant.* 11 | coverage/lcov.info 12 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/best-flutter/disk_lru_cache/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | env: 3 | - SHARD=dartfmt 4 | - SHARD=test 5 | install: 6 | - ./dev/bots/travis_install.sh 7 | script: 8 | - ./dev/bots/travis_script.sh 9 | after_success: 10 | - coveralls-lcov coverage/lcov.info -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: c7ea3ca377e909469c68f2ab878a5bc53d3cf66b 8 | channel: beta 9 | -------------------------------------------------------------------------------- /example/android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /example/.idea/runConfigurations/main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /example/.idea/libraries/Flutter_for_Android.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /dev/bots/travis_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | export PATH=~/development/flutter/bin:$PATH 5 | export ROOT="$PWD" 6 | 7 | if [[ "$SHARD" == "dartfmt" ]]; then 8 | echo 'Formating code' 9 | cd $ROOT 10 | flutter format . || exit $? 11 | else 12 | # tests shard 13 | cd $ROOT 14 | 15 | flutter test --coverage test/* || exit $? 16 | 17 | fi 18 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | 4 | dependencies: 5 | flutter: 6 | sdk: flutter 7 | 8 | disk_lru_cache: 9 | path: ../ 10 | 11 | http: 12 | cupertino_icons: ^0.1.2 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | 19 | 20 | flutter: 21 | uses-material-design: true 22 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /dev/bots/travis_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "$PWD" 4 | export ROOT="$PWD" 5 | 6 | mkdir ~/development 7 | 8 | cd ~/development 9 | wget https://storage.googleapis.com/flutter_infra/releases/beta/linux/flutter_linux_v0.5.1-beta.tar.xz 10 | tar xf ~/development/flutter_linux_v0.5.1-beta.tar.xz 11 | 12 | export PATH=~/development/flutter/bin:$PATH 13 | 14 | 15 | cd $ROOT 16 | flutter packages get 17 | 18 | gem install coveralls-lcov 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.example; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [0.0.2] 2 | 3 | * Use synchronized library. 4 | 5 | 6 | ## [0.0.1] - Basic usage. 7 | 8 | * LRU Map 9 | * Store One `CacheEntry` with multiple files by using `CacheEditor` 10 | * Get `CacheSnapshot` and open streams directly from `CacheEntry` 11 | * Using a key to remove from cache 12 | * A record file to store all operation info,including 'DIRTY','CLEAN','REMOVE','READ' 13 | * Get total size from cache 14 | * When cache.size > caches.maxSize , auto trim to size. 15 | 16 | 17 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: disk_lru_cache 2 | description: Disk lru cache for flutter. 3 | version: 0.0.2 4 | author: JZoom 5 | homepage: https://github.com/best-flutter/disk_lru_cache 6 | 7 | 8 | 9 | environment: 10 | sdk: ">=2.0.0-dev.48.0 <3.0.0" 11 | flutter: ">=0.1.4 <3.0.0" 12 | 13 | 14 | dependencies: 15 | flutter: 16 | sdk: flutter 17 | synchronized: ^2.1.1 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | http: 23 | 24 | flutter: 25 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /example/android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/jzoom/working/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/jzoom/SourceCode/disk_lru_cache/example" 5 | export "FLUTTER_TARGET=lib/main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios" 8 | export "FLUTTER_FRAMEWORK_DIR=/Users/jzoom/working/flutter/bin/cache/artifacts/engine/ios" 9 | export "FLUTTER_BUILD_NAME=1.0.0" 10 | export "FLUTTER_BUILD_NUMBER=1" 11 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.0.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/example.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Xueliang Ren 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:example/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new MyApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /disk_lru_cache.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/example_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | apply plugin: 'com.android.application' 15 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 16 | 17 | android { 18 | compileSdkVersion 27 19 | 20 | lintOptions { 21 | disable 'InvalidPackage' 22 | } 23 | 24 | defaultConfig { 25 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 26 | applicationId "com.example.example" 27 | minSdkVersion 16 28 | targetSdkVersion 27 29 | versionCode 1 30 | versionName "1.0" 31 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 32 | } 33 | 34 | buildTypes { 35 | release { 36 | // TODO: Add your own signing config for the release build. 37 | // Signing with the debug keys for now, so `flutter run --release` works. 38 | signingConfig signingConfigs.debug 39 | } 40 | } 41 | } 42 | 43 | flutter { 44 | source '../..' 45 | } 46 | 47 | dependencies { 48 | testImplementation 'junit:junit:4.12' 49 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 50 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 51 | } 52 | -------------------------------------------------------------------------------- /example/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/android/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 37 | 38 | 39 | 41 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | Build Status 5 | 6 | 7 | Coverage Status 8 | 9 | 10 | PRs Welcome 11 | 12 | 13 | pub package 14 | 15 |

16 | 17 | 18 | 19 | # disk_lru_cache 20 | Disk lru cache for flutter. [wiki](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)) 21 | 22 | A cache that uses a bounded amount of space on a filesystem. 23 | Each cache entry has a string key and a fixed number of files, witch is accessible as stream. 24 | 25 | ## Use cases 26 | 27 | 28 | #### Working with memery 29 | 30 | We provided a `LruMap` ,in order to support LRU order in memory, witch is a subclass of Map.So ,wo can use the `LruMap` just like Map 31 | 32 | 33 | 34 | ``` 35 | final LruMap map = new LruMap(); 36 | 37 | expect(map.values.toList().length, 0); 38 | 39 | map['a'] = 1; 40 | map['b'] = 2; 41 | map['c'] = 3; 42 | 43 | /// use the key 'a' 44 | var f = map['a']; 45 | 46 | /// We use the key 'a', so at this moment it is the last element. 47 | alues = map.values; 48 | expect(values.toList()[0], 2); 49 | expect(values.toList()[1], 3); 50 | expect(values.toList()[2], 1); 51 | 52 | ``` 53 | 54 | 55 | #### Working with file system 56 | 57 | The basic usage is like this: 58 | 59 | 60 | With string: 61 | 62 | ``` 63 | int maxSize = 64 | 10 * 1024 * 1024; // 10M 65 | 66 | // Make sure it's writable 67 | Directory cacheDirectory = 68 | new Directory("${Directory.systemTemp.path}/cache"); 69 | 70 | DiskLruCache cache = new DiskLruCache( 71 | maxSize: maxSize, directory: cacheDirectory, filesCount: 1); 72 | 73 | // write stream 74 | CacheEditor editor = await cache.edit('filekey'); 75 | if(editor!=null){ 76 | IOSink sink = await editor.newSink(0); 77 | sink.write('your value'); 78 | await sink.close(); 79 | await editor.commit(); 80 | } 81 | 82 | // read stream 83 | CacheSnapshot snapshot = await cache.get('filekey'); 84 | String str = await snapshot.getString(0); 85 | print(str); 86 | 87 | ``` 88 | 89 | 90 | With bytes 91 | 92 | ``` 93 | // write bytes 94 | CacheEditor editor = await cache.edit('imagekey'); 95 | if(editor!=null){ 96 | HttpClient client = new HttpClient(); 97 | HttpClientRequest request = await client.openUrl("GET", Uri.parse("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1534075481&di=1a90bd266d62bc5edfe1ce84ac38330e&src=http://photocdn.sohu.com/20130517/Img376200804.jpg")); 98 | HttpClientResponse response = await request.close(); 99 | Stream> stream = await editor.copyStream(0, response); 100 | // The bytes has been written to disk at this point. 101 | await new ByteStream(stream).toBytes(); 102 | await editor.commit(); 103 | 104 | // read stream 105 | CacheSnapshot snapshot = await cache.get('imagekey'); 106 | Uint8List bytes = await snapshot.getBytes(0); 107 | print(bytes); 108 | } 109 | 110 | ``` 111 | 112 | 113 | ## Manage the cache 114 | 115 | 116 | #### Get the bytes of the cache in file system 117 | 118 | ``` 119 | DiskLruCache cache = ...; 120 | print(cache.size) 121 | ``` 122 | 123 | #### Clean the cache 124 | 125 | ``` 126 | DiskLruCache cache = ...; 127 | cache.clean(); 128 | 129 | ``` 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /test/lru_map_test.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:disk_lru_cache/disk_lru_cache.dart'; 5 | 6 | void main() { 7 | test('Lru map', () { 8 | final LruMap map = new LruMap(); 9 | 10 | expect(map.values.toList().length, 0); 11 | 12 | map['a'] = 1; 13 | map['b'] = 2; 14 | map['c'] = 3; 15 | 16 | Iterable values = map.values; 17 | print(values); 18 | 19 | expect(values.toList()[0], 1); 20 | expect(values.toList()[1], 2); 21 | expect(values.toList()[2], 3); 22 | 23 | /// use the key 'a' 24 | var f = map['a']; 25 | 26 | expect(f, 1); 27 | 28 | values = map.values; 29 | print(values); 30 | expect(values.length, 3); 31 | expect(values.length, map.length); 32 | expect(values.toList()[0], 2); 33 | expect(values.toList()[1], 3); 34 | expect(values.toList()[2], 1); 35 | 36 | Iterable keys = map.keys; 37 | 38 | expect(keys.toList()[0], 'b'); 39 | expect(keys.toList()[1], 'c'); 40 | expect(keys.toList()[2], 'a'); 41 | expect(map.isEmpty, false); 42 | expect(map.isNotEmpty, true); 43 | 44 | int value = map.removeHead(); 45 | expect(value, 2); 46 | keys = map.keys; 47 | expect(keys.length, 2); 48 | expect(keys.length, map.length); 49 | expect(keys.toList()[0], 'c'); 50 | expect(keys.toList()[1], 'a'); 51 | 52 | map.remove('a'); 53 | keys = map.keys; 54 | expect(keys.length, 1); 55 | expect(keys.length, map.length); 56 | expect(keys.toList()[0], 'c'); 57 | 58 | bool excuted = false; 59 | //other operation 60 | map.putIfAbsent("d", () { 61 | excuted = true; 62 | return 4; 63 | }); 64 | 65 | keys = map.keys; 66 | expect(keys.length, 2); 67 | expect(keys.length, map.length); 68 | expect(keys.toList()[0], 'c'); 69 | expect(keys.toList()[1], 'd'); 70 | expect(excuted, true); 71 | 72 | excuted = false; 73 | map.putIfAbsent("c", () { 74 | excuted = true; 75 | return 5; 76 | }); 77 | expect(excuted, false); 78 | keys = map.keys; 79 | expect(keys.length, 2); 80 | expect(keys.length, map.length); 81 | expect(keys.toList()[0], 'c'); 82 | expect(keys.toList()[1], 'd'); 83 | 84 | map.update("c", (int value) { 85 | return 4; 86 | }, ifAbsent: () { 87 | return 0; 88 | }); 89 | 90 | expect(map['c'], 4); 91 | 92 | map.update("e", (int value) { 93 | return 8; 94 | }, ifAbsent: () { 95 | return 0; 96 | }); 97 | 98 | expect(map['e'], 0); 99 | 100 | map.updateAll((String key, int value) { 101 | return value + 1; 102 | }); 103 | 104 | expect(map['e'], 1); 105 | 106 | Map casted = map.cast(); 107 | 108 | expect(casted != null, true); 109 | 110 | int now = new DateTime.now().millisecondsSinceEpoch; 111 | for (int i = 0; i < 10000; ++i) { 112 | map['key$i'] = i; 113 | } 114 | 115 | print(new DateTime.now().millisecondsSinceEpoch - now); 116 | 117 | now = new DateTime.now().millisecondsSinceEpoch; 118 | Map org = {}; 119 | for (int i = 0; i < 10000; ++i) { 120 | org['key$i'] = i; 121 | } 122 | 123 | print(new DateTime.now().millisecondsSinceEpoch - now); 124 | }); 125 | 126 | test("test map ", () { 127 | LruMap map = LruMap.of({"a": "1", "b": "2", "c": "3"}); 128 | 129 | expect(map.removeHead(), "1"); 130 | expect(map.removeHead(), "2"); 131 | expect(map.removeHead(), "3"); 132 | expect(map.removeHead(), null); 133 | }); 134 | 135 | test("test clear ", () { 136 | LruMap map = LruMap.of({"a": "1", "b": "2", "c": "3"}); 137 | 138 | expect(map.length, 3); 139 | 140 | map.clear(); 141 | 142 | expect(map.length, 0); 143 | }); 144 | 145 | test("Lru map other", () { 146 | LruMap map = LruMap.of({"a": "1", "b": "2", "c": "3"}); 147 | 148 | LruMap other = new LruMap(); 149 | other.addEntries(map.entries); 150 | 151 | print(other.values); 152 | 153 | expect(other.values.toList()[0], "1"); 154 | expect(other.values.toList()[1], "2"); 155 | expect(other.values.toList()[2], "3"); 156 | expect(other.containsKey('a'), true); 157 | 158 | other.forEach((key, value) { 159 | print(value); 160 | }); 161 | 162 | expect(other.containsValue("1"), true); 163 | 164 | other.removeWhere((key, value) { 165 | return key == "a"; 166 | }); 167 | 168 | expect(other['a'], null); 169 | 170 | expect(other.containsKey('a'), false); 171 | expect(other.containsValue('1'), false); 172 | }); 173 | } 174 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'test_disk_cache.dart'; 4 | 5 | void main() async { 6 | await testCache(); 7 | } 8 | 9 | class MyApp extends StatelessWidget { 10 | // This widget is the root of your application. 11 | @override 12 | Widget build(BuildContext context) { 13 | return new MaterialApp( 14 | title: 'Flutter Demo', 15 | theme: new ThemeData( 16 | // This is the theme of your application. 17 | // 18 | // Try running your application with "flutter run". You'll see the 19 | // application has a blue toolbar. Then, without quitting the app, try 20 | // changing the primarySwatch below to Colors.green and then invoke 21 | // "hot reload" (press "r" in the console where you ran "flutter run", 22 | // or press Run > Flutter Hot Reload in IntelliJ). Notice that the 23 | // counter didn't reset back to zero; the application is not restarted. 24 | primarySwatch: Colors.blue, 25 | ), 26 | home: new MyHomePage(title: 'Flutter Demo Home Page'), 27 | ); 28 | } 29 | } 30 | 31 | class MyHomePage extends StatefulWidget { 32 | MyHomePage({Key key, this.title}) : super(key: key); 33 | 34 | // This widget is the home page of your application. It is stateful, meaning 35 | // that it has a State object (defined below) that contains fields that affect 36 | // how it looks. 37 | 38 | // This class is the configuration for the state. It holds the values (in this 39 | // case the title) provided by the parent (in this case the App widget) and 40 | // used by the build method of the State. Fields in a Widget subclass are 41 | // always marked "final". 42 | 43 | final String title; 44 | 45 | @override 46 | _MyHomePageState createState() => new _MyHomePageState(); 47 | } 48 | 49 | class _MyHomePageState extends State { 50 | int _counter = 0; 51 | 52 | void _incrementCounter() { 53 | setState(() { 54 | // This call to setState tells the Flutter framework that something has 55 | // changed in this State, which causes it to rerun the build method below 56 | // so that the display can reflect the updated values. If we changed 57 | // _counter without calling setState(), then the build method would not be 58 | // called again, and so nothing would appear to happen. 59 | _counter++; 60 | }); 61 | } 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | // This method is rerun every time setState is called, for instance as done 66 | // by the _incrementCounter method above. 67 | // 68 | // The Flutter framework has been optimized to make rerunning build methods 69 | // fast, so that you can just rebuild anything that needs updating rather 70 | // than having to individually change instances of widgets. 71 | return new Scaffold( 72 | appBar: new AppBar( 73 | // Here we take the value from the MyHomePage object that was created by 74 | // the App.build method, and use it to set our appbar title. 75 | title: new Text(widget.title), 76 | ), 77 | body: new Center( 78 | // Center is a layout widget. It takes a single child and positions it 79 | // in the middle of the parent. 80 | child: new Column( 81 | // Column is also layout widget. It takes a list of children and 82 | // arranges them vertically. By default, it sizes itself to fit its 83 | // children horizontally, and tries to be as tall as its parent. 84 | // 85 | // Invoke "debug paint" (press "p" in the console where you ran 86 | // "flutter run", or select "Toggle Debug Paint" from the Flutter tool 87 | // window in IntelliJ) to see the wireframe for each widget. 88 | // 89 | // Column has various properties to control how it sizes itself and 90 | // how it positions its children. Here we use mainAxisAlignment to 91 | // center the children vertically; the main axis here is the vertical 92 | // axis because Columns are vertical (the cross axis would be 93 | // horizontal). 94 | mainAxisAlignment: MainAxisAlignment.center, 95 | children: [ 96 | new Text( 97 | 'You have pushed the button this many times:', 98 | ), 99 | new Text( 100 | '$_counter', 101 | style: Theme.of(context).textTheme.display1, 102 | ), 103 | ], 104 | ), 105 | ), 106 | floatingActionButton: new FloatingActionButton( 107 | onPressed: _incrementCounter, 108 | tooltip: 'Increment', 109 | child: new Icon(Icons.add), 110 | ), // This trailing comma makes auto-formatting nicer for build methods. 111 | ); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /example/lib/test_disk_cache.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'dart:math' as Math; 4 | import 'dart:typed_data'; 5 | 6 | import 'package:disk_lru_cache/_src/disk_lru_cache.dart'; 7 | import 'package:disk_lru_cache/disk_lru_cache.dart'; 8 | import 'package:http/http.dart'; 9 | 10 | Future testCache() async { 11 | int maxSize = 12 | 10 * 1024 * 1024; // 10M,make sure to test rebuild progress below 13 | DiskLruCache cache = new DiskLruCache( 14 | maxSize: maxSize, 15 | directory: new Directory("${Directory.systemTemp.path}/cache"), 16 | filesCount: 1, 17 | opCompactThreshold: 200); 18 | print(cache.directory); 19 | CacheEditor editor; 20 | // write stream 21 | editor = await cache.edit('errorkey'); 22 | if (editor != null) { 23 | IOSink sink = await editor.newSink(0); 24 | sink.write('your value'); 25 | await sink.flush(); 26 | 27 | //remove the file 28 | Iterable values = await cache.values; 29 | values = values.where((CacheEntry entry) { 30 | return entry.key == "errorkey"; 31 | }); 32 | await values.toList()[0].dirtyFiles[0].delete(); 33 | 34 | await sink.close(); 35 | await editor.commit(); 36 | } 37 | 38 | // write stream 39 | editor = await cache.edit('imagekey'); 40 | if (editor != null) { 41 | HttpClient client = new HttpClient(); 42 | HttpClientRequest request = await client.openUrl( 43 | "GET", 44 | Uri.parse( 45 | "https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1534075481&di=1a90bd266d62bc5edfe1ce84ac38330e&src=http://photocdn.sohu.com/20130517/Img376200804.jpg")); 46 | HttpClientResponse response = await request.close(); 47 | Stream> stream = await editor.copyStream(0, response); 48 | // The bytes has been written to disk at this point. 49 | await new ByteStream(stream).toBytes(); 50 | await editor.commit(); 51 | 52 | // read stream 53 | CacheSnapshot snapshot = await cache.get('imagekey'); 54 | Uint8List bytes = await snapshot.getBytes(0); 55 | print(bytes); 56 | } 57 | 58 | String str200k; 59 | 60 | String get200k() { 61 | if (str200k == null) { 62 | StringBuffer sb = new StringBuffer(); 63 | 64 | for (int i = 0, c = 200 * 1024; i < c; ++i) { 65 | sb.write("a"); 66 | } 67 | 68 | str200k = sb.toString(); 69 | } 70 | return str200k; 71 | } 72 | 73 | Future test() async { 74 | // we must wait the file created 75 | List list = []; 76 | List writeDisk = []; 77 | List openWrite = []; 78 | 79 | void editValue(DiskLruCache cache, String key, String value) { 80 | list.add(cache.edit(key).then((CacheEditor editor) { 81 | if (editor != null) { 82 | openWrite.add(editor.newSink(0).then((IOSink sink) async { 83 | writeDisk.add((() async { 84 | if (sink != null) { 85 | sink.write(value); 86 | await sink.close(); 87 | 88 | await editor.commit(); 89 | } else { 90 | print("Sink is null"); 91 | } 92 | })()); 93 | }).catchError((e) { 94 | print(e); 95 | })); 96 | } else { 97 | print("Cannot open editor for key $key"); 98 | } 99 | })); 100 | } 101 | 102 | Future useCache(DiskLruCache cache) async { 103 | int random() { 104 | // 200k * 100 = 20M 105 | return new Math.Random().nextInt(100); 106 | } 107 | 108 | //we open 10 files a time 109 | for (int i = 0; i < 10; ++i) { 110 | editValue(cache, "${random()}", get200k()); 111 | String key = "${random()}"; 112 | cache.get(key).then((CacheSnapshot s) { 113 | if (s == null) { 114 | print('Cache miss $key'); 115 | return; 116 | } 117 | s.getString(0).then((String str) { 118 | print("Cache hit $key=$str"); 119 | }); 120 | }); 121 | //cache.remove("${random()}"); 122 | } 123 | } 124 | 125 | await useCache(cache); 126 | 127 | await Future.wait(list); 128 | await Future.wait(openWrite); 129 | await Future.wait(writeDisk); 130 | } 131 | 132 | // our operation times must > 2000,so that we can test rebuild record file. 133 | for (int i = 0; i < 10; ++i) { 134 | await test(); 135 | } 136 | 137 | Iterable entries = await cache.values; 138 | 139 | List> list = []; 140 | for (CacheEntry entry in entries) { 141 | list.add(cache.remove(entry.key)); 142 | } 143 | await Future.wait(list); 144 | 145 | assert(cache.size == 0); 146 | 147 | await cache.close(); 148 | 149 | print("Cache size : ${cache.size / 1024 / 1024} m "); 150 | 151 | assert(cache.size < maxSize); 152 | } 153 | -------------------------------------------------------------------------------- /flutter_01.log: -------------------------------------------------------------------------------- 1 | Flutter crash report; please file at https://github.com/flutter/flutter/issues. 2 | 3 | ## command 4 | 5 | flutter --no-color packages get 6 | 7 | ## exception 8 | 9 | YamlException: Error on line 14, column 1: Could not find expected ':' for simple key. 10 | s 11 | ^ 12 | 13 | ``` 14 | #0 Scanner._removeSimpleKey (package:yaml/src/scanner.dart:526:7) 15 | #1 Scanner._fetchStreamEnd (package:yaml/src/scanner.dart:598:5) 16 | #2 Scanner._fetchNextToken (package:yaml/src/scanner.dart:356:7) 17 | #3 Scanner._fetchMoreTokens (package:yaml/src/scanner.dart:339:7) 18 | #4 Scanner.peek (package:yaml/src/scanner.dart:317:27) 19 | #5 Parser._parseBlockMappingKey (package:yaml/src/parser.dart:418:26) 20 | #6 Parser._stateMachine (package:yaml/src/parser.dart:86:16) 21 | #7 Parser.parse (package:yaml/src/parser.dart:47:19) 22 | #8 Loader._loadMapping (package:yaml/src/loader.dart:171:23) 23 | #9 Loader._loadNode (package:yaml/src/loader.dart:86:16) 24 | #10 Loader._loadDocument (package:yaml/src/loader.dart:62:20) 25 | #11 Loader.load (package:yaml/src/loader.dart:54:20) 26 | #12 loadYamlDocument (package:yaml/yaml.dart:51:25) 27 | #13 loadYamlNode (package:yaml/yaml.dart:42:5) 28 | #14 loadYaml (package:yaml/yaml.dart:34:5) 29 | #15 FlutterManifest.createFromString (package:flutter_tools/src/flutter_manifest.dart:42:28) 30 | 31 | #16 FlutterManifest.createFromPath (package:flutter_tools/src/flutter_manifest.dart:36:12) 32 | 33 | #17 FlutterProject._readManifest (package:flutter_tools/src/project.dart:120:60) 34 | 35 | #18 FlutterProject.fromDirectory (package:flutter_tools/src/project.dart:41:44) 36 | 37 | #19 FlutterProject.fromPath (package:flutter_tools/src/project.dart:56:58) 38 | #20 PackagesGetCommand.runCommand (package:flutter_tools/src/commands/packages.dart:83:61) 39 | 40 | #21 FlutterCommand.verifyThenRunCommand (package:flutter_tools/src/runner/flutter_command.dart:348:18) 41 | #22 _asyncThenWrapperHelper. (dart:async/runtime/libasync_patch.dart:77:64) 42 | #23 _rootRunUnary (dart:async/zone.dart:1132:38) 43 | #24 _CustomZone.runUnary (dart:async/zone.dart:1029:19) 44 | #25 _FutureListener.handleValue (dart:async/future_impl.dart:129:18) 45 | #26 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:642:45) 46 | #27 Future._propagateToListeners (dart:async/future_impl.dart:671:32) 47 | #28 Future._complete (dart:async/future_impl.dart:476:7) 48 | #29 _SyncCompleter.complete (dart:async/future_impl.dart:51:12) 49 | #30 _AsyncAwaitCompleter.complete. (dart:async/runtime/libasync_patch.dart:33:20) 50 | #31 _rootRun (dart:async/zone.dart:1124:13) 51 | #32 _CustomZone.run (dart:async/zone.dart:1021:19) 52 | #33 _CustomZone.bindCallback. (dart:async/zone.dart:947:23) 53 | #34 _microtaskLoop (dart:async/schedule_microtask.dart:41:21) 54 | #35 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5) 55 | #36 _runPendingImmediateCallback (dart:isolate/runtime/libisolate_patch.dart:114:13) 56 | #37 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:170:5) 57 | ``` 58 | 59 | ## flutter doctor 60 | 61 | ``` 62 | [✓] Flutter (Channel beta, v0.6.0, on Mac OS X 10.13.3 17D47, locale zh-Hans-CN) 63 | • Flutter version 0.6.0 at /Users/jzoom/working/flutter 64 | • Framework revision 9299c02cf7 (4 weeks ago), 2018-08-16 00:35:12 +0200 65 | • Engine revision e3687f70c7 66 | • Dart version 2.1.0-dev.0.0.flutter-be6309690f 67 | 68 | [✓] Android toolchain - develop for Android devices (Android SDK 27.0.3) 69 | • Android SDK at /Users/jzoom/Library/Android/sdk 70 | • Android NDK at /Users/jzoom/Library/Android/sdk/ndk-bundle 71 | • Platform android-27, build-tools 27.0.3 72 | • ANDROID_HOME = /Users/jzoom/Library/Android/sdk 73 | • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java 74 | • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01) 75 | • All Android licenses accepted. 76 | 77 | [✓] iOS toolchain - develop for iOS devices (Xcode 9.4.1) 78 | • Xcode at /Applications/Xcode.app/Contents/Developer 79 | • Xcode 9.4.1, Build version 9F2000 80 | • ios-deploy 1.9.2 81 | • CocoaPods version 1.5.2 82 | 83 | [✓] Android Studio (version 3.1) 84 | • Android Studio at /Applications/Android Studio.app/Contents 85 | • Flutter plugin version 27.1.1 86 | • Dart plugin version 173.4700 87 | • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01) 88 | 89 | [!] IntelliJ IDEA Community Edition (version 2018.1.5) 90 | • IntelliJ at /Applications/IntelliJ IDEA CE.app 91 | ✗ Flutter plugin not installed; this adds Flutter specific functionality. 92 | ✗ Dart plugin not installed; this adds Dart specific functionality. 93 | • For information about installing plugins, see 94 | https://flutter.io/intellij-setup/#installing-the-plugins 95 | 96 | [✓] VS Code (version 1.26.1) 97 | • VS Code at /Applications/Visual Studio Code.app/Contents 98 | • Flutter extension version 2.18.0 99 | 100 | [✓] Connected devices (2 available) 101 | • Android SDK built for x86 • emulator-5554 • android-x86 • Android 8.1.0 (API 27) (emulator) 102 | • iPhone 6s • A797D8CC-2D5D-401B-9634-6D56B30FC76E • ios • iOS 11.4 (simulator) 103 | 104 | ! Doctor found issues in 1 category. 105 | ``` 106 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "2.0.11" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "1.5.2" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "2.4.0" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.0.5" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "1.1.2" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.14.11" 46 | convert: 47 | dependency: transitive 48 | description: 49 | name: convert 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "2.1.1" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "2.1.3" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_test: 66 | dependency: "direct dev" 67 | description: flutter 68 | source: sdk 69 | version: "0.0.0" 70 | http: 71 | dependency: "direct dev" 72 | description: 73 | name: http 74 | url: "https://pub.flutter-io.cn" 75 | source: hosted 76 | version: "0.12.0+4" 77 | http_parser: 78 | dependency: transitive 79 | description: 80 | name: http_parser 81 | url: "https://pub.flutter-io.cn" 82 | source: hosted 83 | version: "3.1.3" 84 | image: 85 | dependency: transitive 86 | description: 87 | name: image 88 | url: "https://pub.flutter-io.cn" 89 | source: hosted 90 | version: "2.1.4" 91 | matcher: 92 | dependency: transitive 93 | description: 94 | name: matcher 95 | url: "https://pub.flutter-io.cn" 96 | source: hosted 97 | version: "0.12.6" 98 | meta: 99 | dependency: transitive 100 | description: 101 | name: meta 102 | url: "https://pub.flutter-io.cn" 103 | source: hosted 104 | version: "1.1.8" 105 | path: 106 | dependency: transitive 107 | description: 108 | name: path 109 | url: "https://pub.flutter-io.cn" 110 | source: hosted 111 | version: "1.6.4" 112 | pedantic: 113 | dependency: transitive 114 | description: 115 | name: pedantic 116 | url: "https://pub.flutter-io.cn" 117 | source: hosted 118 | version: "1.8.0+1" 119 | petitparser: 120 | dependency: transitive 121 | description: 122 | name: petitparser 123 | url: "https://pub.flutter-io.cn" 124 | source: hosted 125 | version: "2.4.0" 126 | quiver: 127 | dependency: transitive 128 | description: 129 | name: quiver 130 | url: "https://pub.flutter-io.cn" 131 | source: hosted 132 | version: "2.0.5" 133 | sky_engine: 134 | dependency: transitive 135 | description: flutter 136 | source: sdk 137 | version: "0.0.99" 138 | source_span: 139 | dependency: transitive 140 | description: 141 | name: source_span 142 | url: "https://pub.flutter-io.cn" 143 | source: hosted 144 | version: "1.5.5" 145 | stack_trace: 146 | dependency: transitive 147 | description: 148 | name: stack_trace 149 | url: "https://pub.flutter-io.cn" 150 | source: hosted 151 | version: "1.9.3" 152 | stream_channel: 153 | dependency: transitive 154 | description: 155 | name: stream_channel 156 | url: "https://pub.flutter-io.cn" 157 | source: hosted 158 | version: "2.0.0" 159 | string_scanner: 160 | dependency: transitive 161 | description: 162 | name: string_scanner 163 | url: "https://pub.flutter-io.cn" 164 | source: hosted 165 | version: "1.0.5" 166 | synchronized: 167 | dependency: "direct main" 168 | description: 169 | name: synchronized 170 | url: "https://pub.flutter-io.cn" 171 | source: hosted 172 | version: "2.1.1" 173 | term_glyph: 174 | dependency: transitive 175 | description: 176 | name: term_glyph 177 | url: "https://pub.flutter-io.cn" 178 | source: hosted 179 | version: "1.1.0" 180 | test_api: 181 | dependency: transitive 182 | description: 183 | name: test_api 184 | url: "https://pub.flutter-io.cn" 185 | source: hosted 186 | version: "0.2.11" 187 | typed_data: 188 | dependency: transitive 189 | description: 190 | name: typed_data 191 | url: "https://pub.flutter-io.cn" 192 | source: hosted 193 | version: "1.1.6" 194 | vector_math: 195 | dependency: transitive 196 | description: 197 | name: vector_math 198 | url: "https://pub.flutter-io.cn" 199 | source: hosted 200 | version: "2.0.8" 201 | xml: 202 | dependency: transitive 203 | description: 204 | name: xml 205 | url: "https://pub.flutter-io.cn" 206 | source: hosted 207 | version: "3.5.0" 208 | sdks: 209 | dart: ">=2.5.0 <3.0.0" 210 | flutter: ">=0.1.4 <3.0.0" 211 | -------------------------------------------------------------------------------- /lib/_src/ioutil.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | /// 6 | /// When a Stream is receiving events, this class also receiving the same events. 7 | /// 8 | class CloseableStream extends Stream implements Closeable { 9 | StreamSubscription _streamSubscription; 10 | 11 | final Stream _stream; 12 | final void Function(T event) onData; 13 | final void Function() onDone; 14 | final Function onError; 15 | 16 | CloseableStream( 17 | this._stream, { 18 | this.onData, 19 | this.onDone, 20 | this.onError, 21 | }); 22 | 23 | @override 24 | StreamSubscription listen(void Function(T event) onData, 25 | {Function onError, void Function() onDone, bool cancelOnError}) { 26 | assert(onData != null); 27 | void Function(T event) _onData; 28 | if (this.onData != null && onData != null) { 29 | _onData = (T event) { 30 | this.onData(event); 31 | onData(event); 32 | }; 33 | } else { 34 | _onData = onData ?? this.onData; 35 | } 36 | 37 | Function _onError; 38 | if (this.onError != null && onError != null) { 39 | _onError = (e) { 40 | this.onError(e); 41 | onError(e); 42 | }; 43 | } else { 44 | _onError = onError ?? this.onError; 45 | } 46 | 47 | void Function() _onDone; 48 | if (this.onDone != null && onDone != null) { 49 | _onDone = () { 50 | this.onDone(); 51 | onDone(); 52 | }; 53 | } else { 54 | _onDone = onDone ?? this.onDone; 55 | } 56 | 57 | try { 58 | _streamSubscription = _stream.listen(_onData, 59 | onError: _onError, onDone: _onDone, cancelOnError: cancelOnError); 60 | return _streamSubscription; 61 | } catch (e) { 62 | _onError(e); 63 | rethrow; 64 | } 65 | } 66 | 67 | @override 68 | Future close() { 69 | if (_streamSubscription == null) { 70 | return new Future.value(); 71 | } 72 | return _streamSubscription.cancel(); 73 | } 74 | } 75 | 76 | class IoUtil { 77 | static Future stream2String( 78 | CloseableStream> stream, Encoding encoding) { 79 | Completer completer = new Completer(); 80 | StringBuffer stringBuffer = new StringBuffer(); 81 | stream.transform(encoding.decoder).listen((String content) { 82 | stringBuffer.write(content); 83 | }, onDone: () { 84 | stream.close(); 85 | completer.complete(stringBuffer.toString()); 86 | }, onError: (e) { 87 | stream.close(); 88 | completer.completeError(e); 89 | }, cancelOnError: true); 90 | 91 | return completer.future; 92 | } 93 | } 94 | 95 | abstract class Closeable { 96 | Future close(); 97 | } 98 | 99 | /// This IOSink do nothing when operation 100 | class EmptyIOSink implements IOSink { 101 | @override 102 | Encoding encoding; 103 | 104 | @override 105 | void add(List data) {} 106 | 107 | @override 108 | void addError(Object error, [StackTrace stackTrace]) {} 109 | 110 | @override 111 | Future addStream(Stream> stream) { 112 | return new Future.value(); 113 | } 114 | 115 | @override 116 | Future close() { 117 | return new Future.value(); 118 | } 119 | 120 | @override 121 | Future get done => new Future.value(); 122 | 123 | @override 124 | Future flush() { 125 | return new Future.value(); 126 | } 127 | 128 | @override 129 | void write(Object obj) {} 130 | 131 | @override 132 | void writeAll(Iterable objects, [String separator = ""]) {} 133 | 134 | @override 135 | void writeCharCode(int charCode) {} 136 | 137 | @override 138 | void writeln([Object obj = ""]) {} 139 | } 140 | 141 | typedef void IOSinkOnError(e); 142 | 143 | /// This IOSink do not throw errors 144 | class IOSinkProxy implements IOSink { 145 | final IOSink sink; 146 | final IOSinkOnError onError; 147 | 148 | IOSinkProxy(this.sink, {this.onError}) 149 | : assert(onError != null), 150 | encoding = sink.encoding; 151 | 152 | @override 153 | Encoding encoding; 154 | 155 | @override 156 | void add(List data) { 157 | try { 158 | sink.add(data); 159 | } catch (e) { 160 | this.onError(e); 161 | } 162 | } 163 | 164 | @override 165 | void addError(Object error, [StackTrace stackTrace]) { 166 | sink.addError(error, stackTrace); 167 | } 168 | 169 | @override 170 | Future addStream(Stream> stream) async { 171 | try { 172 | return await sink.addStream(stream); 173 | } catch (e) { 174 | this.onError(e); 175 | } 176 | } 177 | 178 | @override 179 | Future close() async { 180 | try { 181 | return await sink.close(); 182 | } catch (e) { 183 | this.onError(e); 184 | } 185 | } 186 | 187 | @override 188 | Future get done => sink.done; 189 | 190 | @override 191 | Future flush() async { 192 | try { 193 | return await sink.flush(); 194 | } catch (e) { 195 | this.onError(e); 196 | } 197 | } 198 | 199 | @override 200 | void write(Object obj) { 201 | try { 202 | sink.write(obj); 203 | } catch (e) { 204 | this.onError(e); 205 | } 206 | } 207 | 208 | @override 209 | void writeAll(Iterable objects, [String separator = ""]) { 210 | try { 211 | sink.writeAll(objects, separator); 212 | } catch (e) { 213 | this.onError(e); 214 | } 215 | } 216 | 217 | @override 218 | void writeCharCode(int charCode) { 219 | try { 220 | sink.writeCharCode(charCode); 221 | } catch (e) { 222 | this.onError(e); 223 | } 224 | } 225 | 226 | @override 227 | void writeln([Object obj = ""]) { 228 | try { 229 | sink.writeln(obj); 230 | } catch (e) { 231 | this.onError(e); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "2.0.11" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "1.5.2" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "2.4.0" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.0.5" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "1.1.2" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.14.11" 46 | convert: 47 | dependency: transitive 48 | description: 49 | name: convert 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "2.1.1" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "2.1.3" 60 | cupertino_icons: 61 | dependency: "direct main" 62 | description: 63 | name: cupertino_icons 64 | url: "https://pub.flutter-io.cn" 65 | source: hosted 66 | version: "0.1.2" 67 | disk_lru_cache: 68 | dependency: "direct main" 69 | description: 70 | path: ".." 71 | relative: true 72 | source: path 73 | version: "0.0.2" 74 | flutter: 75 | dependency: "direct main" 76 | description: flutter 77 | source: sdk 78 | version: "0.0.0" 79 | flutter_test: 80 | dependency: "direct dev" 81 | description: flutter 82 | source: sdk 83 | version: "0.0.0" 84 | http: 85 | dependency: "direct main" 86 | description: 87 | name: http 88 | url: "https://pub.flutter-io.cn" 89 | source: hosted 90 | version: "0.12.0+4" 91 | http_parser: 92 | dependency: transitive 93 | description: 94 | name: http_parser 95 | url: "https://pub.flutter-io.cn" 96 | source: hosted 97 | version: "3.1.3" 98 | image: 99 | dependency: transitive 100 | description: 101 | name: image 102 | url: "https://pub.flutter-io.cn" 103 | source: hosted 104 | version: "2.1.4" 105 | matcher: 106 | dependency: transitive 107 | description: 108 | name: matcher 109 | url: "https://pub.flutter-io.cn" 110 | source: hosted 111 | version: "0.12.6" 112 | meta: 113 | dependency: transitive 114 | description: 115 | name: meta 116 | url: "https://pub.flutter-io.cn" 117 | source: hosted 118 | version: "1.1.8" 119 | path: 120 | dependency: transitive 121 | description: 122 | name: path 123 | url: "https://pub.flutter-io.cn" 124 | source: hosted 125 | version: "1.6.4" 126 | pedantic: 127 | dependency: transitive 128 | description: 129 | name: pedantic 130 | url: "https://pub.flutter-io.cn" 131 | source: hosted 132 | version: "1.8.0+1" 133 | petitparser: 134 | dependency: transitive 135 | description: 136 | name: petitparser 137 | url: "https://pub.flutter-io.cn" 138 | source: hosted 139 | version: "2.4.0" 140 | quiver: 141 | dependency: transitive 142 | description: 143 | name: quiver 144 | url: "https://pub.flutter-io.cn" 145 | source: hosted 146 | version: "2.0.5" 147 | sky_engine: 148 | dependency: transitive 149 | description: flutter 150 | source: sdk 151 | version: "0.0.99" 152 | source_span: 153 | dependency: transitive 154 | description: 155 | name: source_span 156 | url: "https://pub.flutter-io.cn" 157 | source: hosted 158 | version: "1.5.5" 159 | stack_trace: 160 | dependency: transitive 161 | description: 162 | name: stack_trace 163 | url: "https://pub.flutter-io.cn" 164 | source: hosted 165 | version: "1.9.3" 166 | stream_channel: 167 | dependency: transitive 168 | description: 169 | name: stream_channel 170 | url: "https://pub.flutter-io.cn" 171 | source: hosted 172 | version: "2.0.0" 173 | string_scanner: 174 | dependency: transitive 175 | description: 176 | name: string_scanner 177 | url: "https://pub.flutter-io.cn" 178 | source: hosted 179 | version: "1.0.5" 180 | synchronized: 181 | dependency: transitive 182 | description: 183 | name: synchronized 184 | url: "https://pub.flutter-io.cn" 185 | source: hosted 186 | version: "2.1.1" 187 | term_glyph: 188 | dependency: transitive 189 | description: 190 | name: term_glyph 191 | url: "https://pub.flutter-io.cn" 192 | source: hosted 193 | version: "1.1.0" 194 | test_api: 195 | dependency: transitive 196 | description: 197 | name: test_api 198 | url: "https://pub.flutter-io.cn" 199 | source: hosted 200 | version: "0.2.11" 201 | typed_data: 202 | dependency: transitive 203 | description: 204 | name: typed_data 205 | url: "https://pub.flutter-io.cn" 206 | source: hosted 207 | version: "1.1.6" 208 | vector_math: 209 | dependency: transitive 210 | description: 211 | name: vector_math 212 | url: "https://pub.flutter-io.cn" 213 | source: hosted 214 | version: "2.0.8" 215 | xml: 216 | dependency: transitive 217 | description: 218 | name: xml 219 | url: "https://pub.flutter-io.cn" 220 | source: hosted 221 | version: "3.5.0" 222 | sdks: 223 | dart: ">=2.5.0 <3.0.0" 224 | flutter: ">=0.1.4 <3.0.0" 225 | -------------------------------------------------------------------------------- /lib/_src/lru_map.dart: -------------------------------------------------------------------------------- 1 | import 'dart:core'; 2 | 3 | /// 4 | /// A collection of key/value pairs, 5 | /// The order of the items in this class dependents on how `recent` we use the key, 6 | /// the more recent we use the key , the order is bigger. 7 | /// 8 | /// Witch means the head of the LRU list is the lest recent visited, 9 | /// and will be more likely to be removed in future 10 | /// 11 | /// For example: 12 | /// LruMap map = new LruMap(); 13 | /// map['a']=1; 14 | /// map['b']=2; 15 | /// map['c']=3; 16 | /// print(map.values); -> 1,2,3 17 | /// 18 | /// var f = map['a']; // this action will take key:a to the tail of the LRU list 19 | /// print(map.values); -> 2,3,1 20 | /// 21 | /// 22 | class LruMap implements Map { 23 | _Entry head; 24 | _Entry tail; 25 | 26 | final Map> _inner = new Map(); 27 | 28 | int get length => _inner.length; 29 | 30 | LruMap(); 31 | 32 | factory LruMap.of(Map map) { 33 | if (map is LruMap) { 34 | return map; 35 | } 36 | LruMap result = new LruMap(); 37 | result.addAll(map); 38 | return result; 39 | } 40 | 41 | Iterable get values { 42 | List list = []; 43 | for (_Entry e = head; e != null; e = e.after) { 44 | list.add(e.value); 45 | } 46 | return list; 47 | } 48 | 49 | Iterable get keys { 50 | List list = []; 51 | for (_Entry e = head; e != null; e = e.after) { 52 | list.add(e.key); 53 | } 54 | return list; 55 | } 56 | 57 | void clear() { 58 | _inner.clear(); 59 | head = tail = null; 60 | } 61 | 62 | @override 63 | V operator [](Object key) { 64 | // 65 | _Entry node = _inner[key]; 66 | if (node == null) return null; 67 | _afterNodeAccess(node); 68 | return node.value; 69 | } 70 | 71 | void _afterNodeRemoval(_Entry e) { 72 | // unlink 73 | _Entry p = e, b = p.before, a = p.after; 74 | p.before = p.after = null; 75 | if (b == null) 76 | head = a; 77 | else 78 | b.after = a; 79 | if (a == null) 80 | tail = b; 81 | else 82 | a.before = b; 83 | } 84 | 85 | V remove(Object key) { 86 | _Entry node = _inner.remove(key); 87 | if (node == null) return null; 88 | _afterNodeRemoval(node); 89 | return node.value; 90 | } 91 | 92 | void _linkNodeLast(_Entry p) { 93 | _Entry last = tail; 94 | tail = p; 95 | if (last == null) 96 | head = p; 97 | else { 98 | p.before = last; 99 | last.after = p; 100 | } 101 | } 102 | 103 | V removeHead() { 104 | _Entry head = this.head; 105 | if (head == null) { 106 | return null; 107 | } 108 | if (head == tail) { 109 | //just one 110 | head.before = head.after = null; 111 | this.head = this.tail = null; 112 | } else { 113 | this.head = head.after; 114 | this.head.before = null; 115 | head.after = null; 116 | } 117 | 118 | _inner.remove(head.key); 119 | 120 | return head.value; 121 | } 122 | 123 | // move to end of the list 124 | void _afterNodeAccess(_Entry e) { 125 | _Entry last; 126 | if ((last = tail) != e) { 127 | _Entry p = e, b = p.before, a = p.after; 128 | p.after = null; 129 | if (b == null) 130 | head = a; 131 | else 132 | b.after = a; 133 | if (a != null) 134 | a.before = b; 135 | else 136 | last = b; 137 | if (last == null) 138 | head = p; 139 | else { 140 | p.before = last; 141 | last.after = p; 142 | } 143 | tail = p; 144 | } 145 | } 146 | 147 | @override 148 | void addAll(Map other) { 149 | assert(other != null); 150 | other.forEach((K key, V value) { 151 | this[key] = value; 152 | }); 153 | } 154 | 155 | @override 156 | void addEntries(Iterable> newEntries) { 157 | newEntries.forEach((MapEntry entry) { 158 | this[entry.key] = entry.value; 159 | }); 160 | } 161 | 162 | @override 163 | bool containsKey(Object key) { 164 | return _inner.containsKey(key); 165 | } 166 | 167 | @override 168 | bool containsValue(Object value) { 169 | for (_Entry e = head; e != null; e = e.after) { 170 | if (e.value == value) { 171 | return true; 172 | } 173 | } 174 | return false; 175 | } 176 | 177 | @override 178 | bool get isEmpty => _inner.isEmpty; 179 | 180 | @override 181 | bool get isNotEmpty => _inner.isNotEmpty; 182 | 183 | @override 184 | void forEach(void Function(K key, V value) f) { 185 | for (_Entry e = head; e != null; e = e.after) { 186 | f(e.key, e.value); 187 | } 188 | } 189 | 190 | @override 191 | void removeWhere(bool Function(K key, V value) predicate) { 192 | _inner.removeWhere((K _key, _Entry _value) { 193 | if (predicate(_key, _value.value)) { 194 | _afterNodeRemoval(_value); 195 | return true; 196 | } 197 | return false; 198 | }); 199 | } 200 | 201 | @override 202 | Iterable> get entries { 203 | List> list = []; 204 | for (_Entry e = head; e != null; e = e.after) { 205 | list.add(new MapEntry(e.key, e.value)); 206 | } 207 | return list; 208 | } 209 | 210 | _Entry _createNew(K key, V value) { 211 | _Entry entry = new _Entry(key: key, value: value); 212 | _linkNodeLast(entry); 213 | return entry; 214 | } 215 | 216 | void operator []=(K key, dynamic value) { 217 | _Entry node = _inner[key]; 218 | if (node == null) { 219 | _inner[key] = _createNew(key, value); 220 | } else { 221 | //new Node 222 | _afterNodeAccess(node); 223 | } 224 | } 225 | 226 | @override 227 | V putIfAbsent(K key, V Function() ifAbsent) { 228 | assert(ifAbsent != null); 229 | return _inner.putIfAbsent(key, () { 230 | V value = ifAbsent(); 231 | return _createNew(key, value); 232 | })?.value; 233 | } 234 | 235 | @override 236 | V update(K key, V Function(V value) update, {V Function() ifAbsent}) { 237 | assert(update != null); 238 | var updateFunc = (_Entry _value) { 239 | V value = update(_value.value); 240 | _value.value = value; 241 | _afterNodeAccess(_value); 242 | return _value; 243 | }; 244 | if (ifAbsent != null) { 245 | return _inner.update(key, updateFunc, ifAbsent: () { 246 | V value = ifAbsent(); 247 | return _createNew(key, value); 248 | })?.value; 249 | } else { 250 | return _inner.update(key, updateFunc)?.value; 251 | } 252 | } 253 | 254 | @override 255 | void updateAll(V Function(K key, V value) update) { 256 | assert(update != null); 257 | _inner.updateAll((K _key, _Entry _value) { 258 | V value = update(_key, _value.value); 259 | _value.value = value; 260 | 261 | /// update all values,we need to update all element orders, 262 | /// witch is not necessary here. 263 | 264 | return _value; 265 | }); 266 | } 267 | 268 | Map retype() { 269 | throw new Exception("Not implement"); 270 | } 271 | 272 | @override 273 | Map cast() { 274 | return _inner.cast(); 275 | } 276 | 277 | @override 278 | Map map(MapEntry Function(K key, V value) f) { 279 | throw new Exception("Not implement"); 280 | } 281 | } 282 | 283 | /// Store key and value 284 | class _Entry { 285 | final K key; 286 | V value; 287 | 288 | _Entry before; 289 | _Entry after; 290 | 291 | _Entry({this.key, this.value}); 292 | } 293 | -------------------------------------------------------------------------------- /test/disk_lru_cache_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:disk_lru_cache/_src/disk_lru_cache.dart'; 6 | import 'package:http/http.dart'; 7 | import 'package:flutter_test/flutter_test.dart'; 8 | import 'dart:math' as Math; 9 | import 'package:disk_lru_cache/disk_lru_cache.dart'; 10 | 11 | void main() { 12 | int maxSize = 13 | 10 * 1024 * 1024; // 10M,make sure to test rebuild progress below 14 | 15 | Directory cacheDirectory = 16 | new Directory("${Directory.systemTemp.path}/cache"); 17 | 18 | test("Basic usage with bytes", () async { 19 | DiskLruCache cache = new DiskLruCache( 20 | maxSize: maxSize, 21 | directory: cacheDirectory, 22 | filesCount: 1, 23 | opCompactThreshold: 200); 24 | 25 | print('============================\n${cacheDirectory.path}'); 26 | 27 | // write stream 28 | CacheEditor editor = await cache.edit('imagekey'); 29 | if (editor != null) { 30 | HttpClient client = new HttpClient(); 31 | HttpClientRequest request = await client.openUrl( 32 | "GET", 33 | Uri.parse( 34 | "https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1534075481&di=1a90bd266d62bc5edfe1ce84ac38330e&src=http://photocdn.sohu.com/20130517/Img376200804.jpg")); 35 | HttpClientResponse response = await request.close(); 36 | Stream> stream = await editor.copyStream(0, response); 37 | // The bytes has been written to disk at this point. 38 | await new ByteStream(stream).toBytes(); 39 | await editor.commit(); 40 | 41 | // read stream 42 | CacheSnapshot snapshot = await cache.get('imagekey'); 43 | Uint8List bytes = await snapshot.getBytes(0); 44 | print(bytes.length); 45 | } 46 | }); 47 | 48 | test("Basic usage width string", () async { 49 | DiskLruCache cache = new DiskLruCache( 50 | maxSize: maxSize, 51 | directory: cacheDirectory, 52 | filesCount: 1, 53 | opCompactThreshold: 200); 54 | 55 | // write stream 56 | CacheEditor editor = await cache.edit('filekey'); 57 | if (editor != null) { 58 | IOSink sink = await editor.newSink(0); 59 | sink.write('your value'); 60 | await sink.close(); 61 | await editor.commit(); 62 | } 63 | 64 | // read stream 65 | CacheSnapshot snapshot = await cache.get('filekey'); 66 | String str = await snapshot.getString(0); 67 | print(str); 68 | }); 69 | 70 | Future testCache() async { 71 | DiskLruCache cache = new DiskLruCache( 72 | maxSize: maxSize, 73 | directory: cacheDirectory, 74 | filesCount: 1, 75 | opCompactThreshold: 200); 76 | print(cache.directory); 77 | 78 | String str200k; 79 | String get200k() { 80 | if (str200k == null) { 81 | StringBuffer sb = new StringBuffer(); 82 | 83 | for (int i = 0, c = 200 * 1024; i < c; ++i) { 84 | sb.write("a"); 85 | } 86 | 87 | str200k = sb.toString(); 88 | } 89 | return str200k; 90 | } 91 | 92 | Future test() async { 93 | // we must wait the file created 94 | List list = []; 95 | List writeDisk = []; 96 | List openWrite = []; 97 | 98 | void editValue(DiskLruCache cache, String key, String value) { 99 | list.add(cache.edit(key).then((CacheEditor editor) { 100 | if (editor != null) { 101 | openWrite.add(editor.newSink(0).then((IOSink sink) async { 102 | writeDisk.add((() async { 103 | if (sink != null) { 104 | sink.write(value); 105 | await sink.close(); 106 | 107 | await editor.commit(); 108 | } else { 109 | print("Sink is null"); 110 | } 111 | })()); 112 | }).catchError((e) { 113 | print(e); 114 | })); 115 | } else { 116 | print("Cannot open editor for key $key"); 117 | } 118 | })); 119 | } 120 | 121 | Future useCache(DiskLruCache cache) async { 122 | int random() { 123 | // 200k * 100 = 20M 124 | return new Math.Random().nextInt(100); 125 | } 126 | 127 | //we open 10 files at the same time 128 | for (int i = 0; i < 10; ++i) { 129 | editValue(cache, "${random()}", get200k()); 130 | String key = "${random()}"; 131 | cache.get(key).then((CacheSnapshot s) { 132 | if (s == null) { 133 | print('Cache miss $key'); 134 | return; 135 | } 136 | s.getString(0).then((String str) { 137 | print("Cache hit $key"); 138 | }); 139 | }); 140 | //cache.remove("${random()}"); 141 | } 142 | } 143 | 144 | await useCache(cache); 145 | 146 | await Future.wait(list); 147 | await Future.wait(openWrite); 148 | await Future.wait(writeDisk); 149 | } 150 | 151 | // our operation times must > 2000,so that we can test rebuild record file. 152 | for (int i = 0; i < 10; ++i) { 153 | await test(); 154 | } 155 | 156 | 157 | Iterable entries = await cache.values; 158 | int calcSize = 0; 159 | entries.forEach((CacheEntry entry) { 160 | calcSize += entry.size; 161 | }); 162 | 163 | expect(cache.size, calcSize); 164 | 165 | expect(cache.size < maxSize, true); 166 | 167 | await cache.close(); 168 | print("Cache size : ${cache.size / 1024 / 1024} m "); 169 | } 170 | 171 | Future testRemoveAll() async { 172 | DiskLruCache cache = new DiskLruCache( 173 | maxSize: maxSize, directory: cacheDirectory, filesCount: 1); 174 | List results = await cache.clean(); 175 | expect(results.every((bool value) => value), true); 176 | expect(cache.size, 0); 177 | } 178 | 179 | test('Lru cache', () async { 180 | await (() async { 181 | await testCache(); 182 | })(); 183 | 184 | // do it again 185 | await (() async { 186 | await testCache(); 187 | })(); 188 | 189 | //test remove 190 | await (() async { 191 | await testRemoveAll(); 192 | })(); 193 | }); 194 | 195 | test("Test commit errors", () async { 196 | DiskLruCache cache = new DiskLruCache( 197 | maxSize: maxSize, directory: cacheDirectory, filesCount: 2); 198 | // write stream 199 | CacheEditor editor = await cache.edit('filekey'); 200 | if (editor != null) { 201 | IOSink sink = await editor.newSink(0); 202 | sink.write('your value'); 203 | await sink.close(); 204 | await editor.commit(); 205 | 206 | CacheSnapshot snapshot = await cache.get("filekey"); 207 | expect(snapshot, null); 208 | } 209 | }); 210 | 211 | test("Simulate errors when write to disk", () async { 212 | DiskLruCache cache = new DiskLruCache( 213 | maxSize: maxSize, directory: cacheDirectory, filesCount: 1); 214 | // write stream 215 | CacheEditor editor = await cache.edit('errorkey'); 216 | if (editor != null) { 217 | IOSink sink = await editor.newSink(0); 218 | 219 | 220 | sink.write('your value'); 221 | await sink.flush(); 222 | 223 | //remove the file 224 | Iterable values = await cache.values; 225 | values = values.where((CacheEntry entry) { 226 | return entry.key == "errorkey"; 227 | }); 228 | await values.toList()[0].dirtyFiles[0].delete(); 229 | 230 | await sink.close(); 231 | await editor.commit(); 232 | 233 | expect(await cache.get("errorkey"), null); 234 | } 235 | }); 236 | 237 | test("Delete file when read", () async { 238 | //Delete file when read 239 | 240 | DiskLruCache cache = new DiskLruCache( 241 | maxSize: maxSize, directory: cacheDirectory, filesCount: 1); 242 | 243 | CacheEditor editor = await cache.edit('readkey'); 244 | if (editor != null) { 245 | IOSink sink = await editor.newSink(0); 246 | 247 | sink.write('your value'); 248 | await sink.flush(); 249 | await sink.close(); 250 | await editor.commit(); 251 | 252 | //remove the file 253 | Iterable values = await cache.values; 254 | values = values.where((CacheEntry entry) { 255 | return entry.key == "readkey"; 256 | }); 257 | try { 258 | await values.toList()[0].cleanFiles[0].delete(); 259 | } catch (e) { 260 | print(e); 261 | } 262 | 263 | expect(await cache.get("readkey"), null); 264 | } 265 | }); 266 | 267 | test("Leave some dirty and don't handle and close the cache", () async { 268 | DiskLruCache cache = new DiskLruCache( 269 | maxSize: maxSize, directory: cacheDirectory, filesCount: 1); 270 | 271 | CacheEditor editor = await cache.edit('readkey'); 272 | 273 | await cache.close(); 274 | 275 | cache = new DiskLruCache( 276 | maxSize: maxSize, directory: cacheDirectory, filesCount: 1); 277 | 278 | editor = await cache.edit("readkey"); 279 | IOSink sink = await editor.newSink(0); 280 | 281 | sink.write('your value'); 282 | await sink.flush(); 283 | await sink.close(); 284 | await editor.commit(); 285 | }); 286 | } 287 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_Packages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | -------------------------------------------------------------------------------- /example/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |