pre work for web support: bindings separation, native preparations
This commit is contained in:
parent
68d1e05288
commit
595501ca1d
12 changed files with 316 additions and 210 deletions
|
@ -1,10 +1,10 @@
|
|||
# Run with `flutter pub run ffigen --config ffigen_linux.yaml`.
|
||||
name: StockfishChessEngineBindings
|
||||
name: StockfishChessEngineCBindings
|
||||
description: |
|
||||
Bindings for `src/stockfish.h`.
|
||||
|
||||
Regenerate bindings with `dart run ffigen --config ffigen.yaml`.
|
||||
output: "lib/stockfish_bindings_generated.dart"
|
||||
output: "lib/stockfish_c_bindings_generated.dart"
|
||||
headers:
|
||||
entry-points:
|
||||
- "src/stockfish.h"
|
||||
|
|
|
@ -1,75 +1,27 @@
|
|||
// Using code from https://github.com/ArjanAswal/Stockfish/blob/master/lib/src/stockfish.dart
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'dart:isolate';
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter_stockfish_plugin/stockfish_bindings.dart';
|
||||
import 'package:flutter_stockfish_plugin/stockfish_native_bindings.dart'
|
||||
if (dart.library.html) 'package:flutter_stockfish_plugin/stockfish_web_bindings.dart';
|
||||
import 'package:flutter_stockfish_plugin/stockfish_state.dart';
|
||||
|
||||
import 'stockfish_bindings_generated.dart';
|
||||
import 'stockfish_state.dart';
|
||||
final StockfishChessEngineAbstractBindings _bindings =
|
||||
StockfishChessEngineBindings();
|
||||
|
||||
const String _libName = 'flutter_stockfish_plugin';
|
||||
//const String _releaseType = kDebugMode ? 'Debug' : 'Release';
|
||||
|
||||
/// The dynamic library in which the symbols for [StockfishChessEngineBindings] can be found.
|
||||
final DynamicLibrary _dylib = () {
|
||||
if (Platform.isMacOS || Platform.isIOS) {
|
||||
return DynamicLibrary.open('$_libName.framework/$_libName');
|
||||
}
|
||||
if (Platform.isAndroid || Platform.isLinux) {
|
||||
return DynamicLibrary.open('lib$_libName.so');
|
||||
}
|
||||
if (Platform.isWindows) {
|
||||
return DynamicLibrary.open('$_libName.dll');
|
||||
}
|
||||
throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
|
||||
}();
|
||||
|
||||
/// The bindings to the native functions in [_dylib].
|
||||
final StockfishChessEngineBindings _bindings =
|
||||
StockfishChessEngineBindings(_dylib);
|
||||
|
||||
/// A wrapper for C++ engine.
|
||||
class Stockfish {
|
||||
final Completer<Stockfish>? completer;
|
||||
final _state = StockfishStateClass();
|
||||
|
||||
final _state = _StockfishState();
|
||||
final _stdoutController = StreamController<String>.broadcast();
|
||||
final _mainPort = ReceivePort();
|
||||
final _stdoutPort = ReceivePort();
|
||||
|
||||
late StreamSubscription _mainSubscription;
|
||||
late StreamSubscription _stdoutSubscription;
|
||||
|
||||
Stockfish._({this.completer}) {
|
||||
_mainSubscription =
|
||||
_mainPort.listen((message) => _cleanUp(message is int ? message : 1));
|
||||
_stdoutSubscription = _stdoutPort.listen((message) {
|
||||
if (message is String) {
|
||||
_stdoutController.sink.add(message);
|
||||
} else {
|
||||
developer.log('The stdout isolate sent $message', name: 'Stockfish');
|
||||
}
|
||||
Stockfish._() {
|
||||
_state.setValue(StockfishState.starting);
|
||||
_bindings.stockfishMain(() {
|
||||
_state.setValue(StockfishState.ready);
|
||||
}).then((exitCode) {
|
||||
_state.setValue(
|
||||
exitCode == 0 ? StockfishState.disposed : StockfishState.error);
|
||||
_instance = null;
|
||||
}, onError: (error) {
|
||||
_state.setValue(StockfishState.error);
|
||||
_instance = null;
|
||||
});
|
||||
compute(_spawnIsolates, [_mainPort.sendPort, _stdoutPort.sendPort]).then(
|
||||
(success) {
|
||||
final state = success ? StockfishState.ready : StockfishState.error;
|
||||
_state._setValue(state);
|
||||
if (state == StockfishState.ready) {
|
||||
completer?.complete(this);
|
||||
}
|
||||
},
|
||||
onError: (error) {
|
||||
developer.log('The init isolate encountered an error $error',
|
||||
name: 'Stockfish');
|
||||
_cleanUp(1);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Stockfish? _instance;
|
||||
|
@ -82,7 +34,6 @@ class Stockfish {
|
|||
if (_instance != null) {
|
||||
throw StateError('Multiple instances are not supported, yet.');
|
||||
}
|
||||
|
||||
_instance = Stockfish._();
|
||||
return _instance!;
|
||||
}
|
||||
|
@ -91,31 +42,27 @@ class Stockfish {
|
|||
ValueListenable<StockfishState> get state => _state;
|
||||
|
||||
/// The standard output stream.
|
||||
Stream<String> get stdout => _stdoutController.stream;
|
||||
Stream<String> get stdout => _bindings.read;
|
||||
|
||||
/// The standard input sink.
|
||||
set stdin(String line) {
|
||||
final stateValue = _state.value;
|
||||
final stateValue = state.value;
|
||||
if (stateValue != StockfishState.ready) {
|
||||
throw StateError('Stockfish is not ready ($stateValue)');
|
||||
}
|
||||
|
||||
final unicodePointer = '$line\n'.toNativeUtf8();
|
||||
final pointer = unicodePointer.cast<Char>();
|
||||
_bindings.stockfish_stdin_write(pointer);
|
||||
calloc.free(unicodePointer);
|
||||
_bindings.write(line);
|
||||
}
|
||||
|
||||
/// Stops the C++ engine.
|
||||
void dispose() {
|
||||
final stateValue = _state.value;
|
||||
final stateValue = state.value;
|
||||
if (stateValue == StockfishState.ready) {
|
||||
stdin = 'quit';
|
||||
}
|
||||
}
|
||||
|
||||
void _cleanUp(int exitCode) {
|
||||
_stdoutController.close();
|
||||
/*_stdoutController.close();
|
||||
|
||||
_mainSubscription.cancel();
|
||||
_stdoutSubscription.cancel();
|
||||
|
@ -123,95 +70,6 @@ class Stockfish {
|
|||
_state._setValue(
|
||||
exitCode == 0 ? StockfishState.disposed : StockfishState.error);
|
||||
|
||||
_instance = null;
|
||||
_instance = null;*/
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a C++ engine asynchronously.
|
||||
///
|
||||
/// This method is different from the factory method [Stockfish] that
|
||||
/// it will wait for the engine to be ready before returning the instance.
|
||||
Future<Stockfish> stockfishAsync() {
|
||||
if (Stockfish._instance != null) {
|
||||
return Future.error(StateError('Only one instance can be used at a time'));
|
||||
}
|
||||
|
||||
final completer = Completer<Stockfish>();
|
||||
Stockfish._instance = Stockfish._(completer: completer);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
class _StockfishState extends ChangeNotifier
|
||||
implements ValueListenable<StockfishState> {
|
||||
StockfishState _value = StockfishState.starting;
|
||||
|
||||
@override
|
||||
StockfishState get value => _value;
|
||||
|
||||
_setValue(StockfishState v) {
|
||||
if (v == _value) return;
|
||||
_value = v;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void _isolateMain(SendPort mainPort) {
|
||||
final exitCode = _bindings.stockfish_main();
|
||||
mainPort.send(exitCode);
|
||||
|
||||
developer.log('nativeMain returns $exitCode', name: 'Stockfish');
|
||||
}
|
||||
|
||||
void _isolateStdout(SendPort stdoutPort) {
|
||||
String previous = '';
|
||||
|
||||
while (true) {
|
||||
final pointer = _bindings.stockfish_stdout_read();
|
||||
|
||||
if (pointer.address == 0) {
|
||||
developer.log('nativeStdoutRead returns NULL', name: 'Stockfish');
|
||||
return;
|
||||
}
|
||||
|
||||
Uint8List newContentCharList;
|
||||
|
||||
final newContentLength = pointer.cast<Utf8>().length;
|
||||
newContentCharList = Uint8List.view(
|
||||
pointer.cast<Uint8>().asTypedList(newContentLength).buffer,
|
||||
0,
|
||||
newContentLength);
|
||||
|
||||
final newContent = utf8.decode(newContentCharList);
|
||||
|
||||
final data = previous + newContent;
|
||||
final lines = data.split('\n');
|
||||
previous = lines.removeLast();
|
||||
for (final line in lines) {
|
||||
stdoutPort.send(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _spawnIsolates(List<SendPort> mainAndStdout) async {
|
||||
final initResult = _bindings.stockfish_init();
|
||||
if (initResult != 0) {
|
||||
developer.log('initResult=$initResult', name: 'Stockfish');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await Isolate.spawn(_isolateStdout, mainAndStdout[1]);
|
||||
} catch (error) {
|
||||
developer.log('Failed to spawn stdout isolate: $error', name: 'Stockfish');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await Isolate.spawn(_isolateMain, mainAndStdout[0]);
|
||||
} catch (error) {
|
||||
developer.log('Failed to spawn main isolate: $error', name: 'Stockfish');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
9
lib/stockfish_bindings.dart
Normal file
9
lib/stockfish_bindings.dart
Normal file
|
@ -0,0 +1,9 @@
|
|||
import 'dart:async';
|
||||
|
||||
abstract class StockfishChessEngineAbstractBindings {
|
||||
final stdoutController = StreamController<String>.broadcast();
|
||||
Future<int> stockfishMain(Function active);
|
||||
void cleanUp(int exitCode);
|
||||
void write(String line);
|
||||
Stream<String> get read => stdoutController.stream;
|
||||
}
|
|
@ -5,23 +5,24 @@
|
|||
// AUTO GENERATED FILE, DO NOT EDIT.
|
||||
//
|
||||
// Generated by `package:ffigen`.
|
||||
// ignore_for_file: type=lint
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
/// Bindings for `src/stockfish.h`.
|
||||
///
|
||||
/// Regenerate bindings with `dart run ffigen --config ffigen.yaml`.
|
||||
///
|
||||
class StockfishChessEngineBindings {
|
||||
class StockfishChessEngineCBindings {
|
||||
/// Holds the symbol lookup function.
|
||||
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
|
||||
_lookup;
|
||||
|
||||
/// The symbols are looked up in [dynamicLibrary].
|
||||
StockfishChessEngineBindings(ffi.DynamicLibrary dynamicLibrary)
|
||||
StockfishChessEngineCBindings(ffi.DynamicLibrary dynamicLibrary)
|
||||
: _lookup = dynamicLibrary.lookup;
|
||||
|
||||
/// The symbols are looked up with [lookup].
|
||||
StockfishChessEngineBindings.fromLookup(
|
||||
StockfishChessEngineCBindings.fromLookup(
|
||||
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
|
||||
lookup)
|
||||
: _lookup = lookup;
|
||||
|
@ -56,15 +57,19 @@ class StockfishChessEngineBindings {
|
|||
late final _stockfish_stdin_write = _stockfish_stdin_writePtr
|
||||
.asFunction<int Function(ffi.Pointer<ffi.Char>)>();
|
||||
|
||||
ffi.Pointer<ffi.Char> stockfish_stdout_read() {
|
||||
return _stockfish_stdout_read();
|
||||
ffi.Pointer<ffi.Char> stockfish_stdout_read(
|
||||
int trygetline,
|
||||
) {
|
||||
return _stockfish_stdout_read(
|
||||
trygetline,
|
||||
);
|
||||
}
|
||||
|
||||
late final _stockfish_stdout_readPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(
|
||||
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function(ffi.Int)>>(
|
||||
'stockfish_stdout_read');
|
||||
late final _stockfish_stdout_read =
|
||||
_stockfish_stdout_readPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
|
||||
late final _stockfish_stdout_read = _stockfish_stdout_readPtr
|
||||
.asFunction<ffi.Pointer<ffi.Char> Function(int)>();
|
||||
}
|
||||
|
||||
typedef ssize_t = __ssize_t;
|
148
lib/stockfish_native_bindings.dart
Normal file
148
lib/stockfish_native_bindings.dart
Normal file
|
@ -0,0 +1,148 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:flutter_stockfish_plugin/stockfish_bindings.dart';
|
||||
import 'package:flutter_stockfish_plugin/stockfish_c_bindings_generated.dart';
|
||||
|
||||
const String _libName = 'flutter_stockfish_plugin';
|
||||
|
||||
/// The dynamic library in which the symbols for [StockfishChessEngineCBindings] can be found.
|
||||
final DynamicLibrary _dylib = () {
|
||||
if (Platform.isMacOS || Platform.isIOS) {
|
||||
return DynamicLibrary.open('$_libName.framework/$_libName');
|
||||
}
|
||||
if (Platform.isAndroid || Platform.isLinux) {
|
||||
return DynamicLibrary.open('lib$_libName.so');
|
||||
}
|
||||
if (Platform.isWindows) {
|
||||
return DynamicLibrary.open('$_libName.dll');
|
||||
}
|
||||
throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
|
||||
}();
|
||||
|
||||
/// The bindings to the native functions in [_dylib].
|
||||
final StockfishChessEngineCBindings _bindings =
|
||||
StockfishChessEngineCBindings(_dylib);
|
||||
|
||||
class StockfishChessEngineBindings
|
||||
extends StockfishChessEngineAbstractBindings {
|
||||
final _mainPort = ReceivePort();
|
||||
final _stdoutPort = ReceivePort();
|
||||
late StreamSubscription _mainSubscription;
|
||||
late StreamSubscription _stdoutSubscription;
|
||||
|
||||
@override
|
||||
Future<int> stockfishMain(Function active) {
|
||||
final completer = Completer<int>();
|
||||
_mainSubscription = _mainPort.listen((message) {
|
||||
cleanUp(message is int ? message : 1);
|
||||
completer.complete(message is int ? message : 1);
|
||||
});
|
||||
_stdoutSubscription = _stdoutPort.listen((message) {
|
||||
if (message is String) {
|
||||
stdoutController.sink.add(message);
|
||||
} else {
|
||||
developer.log('The stdout isolate sent $message', name: 'Stockfish');
|
||||
}
|
||||
});
|
||||
compute(_spawnIsolates, [_mainPort.sendPort, _stdoutPort.sendPort]).then(
|
||||
(success) {
|
||||
if (success) {
|
||||
active();
|
||||
} else {
|
||||
completer.completeError('Unable to create Isolates');
|
||||
cleanUp(1);
|
||||
}
|
||||
},
|
||||
onError: (error) {
|
||||
developer.log('The init isolate encountered an error $error',
|
||||
name: 'Stockfish');
|
||||
cleanUp(1);
|
||||
},
|
||||
);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(String line) {
|
||||
final unicodePointer = '$line\n'.toNativeUtf8();
|
||||
final pointer = unicodePointer.cast<Char>();
|
||||
_bindings.stockfish_stdin_write(pointer);
|
||||
calloc.free(unicodePointer);
|
||||
}
|
||||
|
||||
@override
|
||||
void cleanUp(int exitCode) {
|
||||
stdoutController.close();
|
||||
_mainSubscription.cancel();
|
||||
_stdoutSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void _isolateMain(SendPort mainPort) {
|
||||
final exitCode = _bindings.stockfish_main();
|
||||
mainPort.send(exitCode);
|
||||
|
||||
developer.log('nativeMain returns $exitCode', name: 'Stockfish');
|
||||
}
|
||||
|
||||
void _isolateStdout(SendPort stdoutPort) {
|
||||
String previous = '';
|
||||
|
||||
while (true) {
|
||||
final pointer = _bindings.stockfish_stdout_read(0);
|
||||
|
||||
if (pointer.address == 0) {
|
||||
developer.log('nativeStdoutRead returns NULL', name: 'Stockfish');
|
||||
return;
|
||||
}
|
||||
|
||||
Uint8List newContentCharList;
|
||||
|
||||
final newContentLength = pointer.cast<Utf8>().length;
|
||||
newContentCharList = Uint8List.view(
|
||||
pointer.cast<Uint8>().asTypedList(newContentLength).buffer,
|
||||
0,
|
||||
newContentLength);
|
||||
|
||||
final newContent = utf8.decode(newContentCharList);
|
||||
|
||||
final data = previous + newContent;
|
||||
final lines = data.split('\n');
|
||||
previous = lines.removeLast();
|
||||
for (final line in lines) {
|
||||
stdoutPort.send(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _spawnIsolates(List<SendPort> mainAndStdout) async {
|
||||
final initResult = _bindings.stockfish_init();
|
||||
if (initResult != 0) {
|
||||
developer.log('initResult=$initResult', name: 'Stockfish');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await Isolate.spawn(_isolateStdout, mainAndStdout[1]);
|
||||
} catch (error) {
|
||||
developer.log('Failed to spawn stdout isolate: $error', name: 'Stockfish');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await Isolate.spawn(_isolateMain, mainAndStdout[0]);
|
||||
} catch (error) {
|
||||
developer.log('Failed to spawn main isolate: $error', name: 'Stockfish');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,4 +1,20 @@
|
|||
// Taken from https://github.com/ArjanAswal/Stockfish/blob/master/lib/src/stockfish_state.dart
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class StockfishStateClass extends ChangeNotifier
|
||||
implements ValueListenable<StockfishState> {
|
||||
StockfishState _value = StockfishState.starting;
|
||||
|
||||
@override
|
||||
StockfishState get value => _value;
|
||||
|
||||
setValue(StockfishState v) {
|
||||
if (v == _value) return;
|
||||
_value = v;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// The following is taken from https://github.com/ArjanAswal/Stockfish/blob/master/lib/src/stockfish_state.dart
|
||||
|
||||
/// C++ engine state.
|
||||
enum StockfishState {
|
||||
|
|
25
lib/stockfish_web_bindings.dart
Normal file
25
lib/stockfish_web_bindings.dart
Normal file
|
@ -0,0 +1,25 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_stockfish_plugin/stockfish_bindings.dart';
|
||||
|
||||
class StockfishChessEngineBindings
|
||||
extends StockfishChessEngineAbstractBindings {
|
||||
@override
|
||||
void cleanUp(int exitCode) {
|
||||
// TODO: implement cleanUp
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> stockfishMain(Function active) {
|
||||
// TODO: implement stockfishMain
|
||||
final completer = Completer<int>();
|
||||
//completer.complete(0);
|
||||
active();
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(String line) {
|
||||
// TODO: implement write
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ set(CMAKE_CXX_STANDARD 17)
|
|||
if(MSVC)
|
||||
set(COMMON_FLAGS "/std:c++17 /LTCG")
|
||||
|
||||
set(SIMD_FLAGS "/arch:AVX2 /arch:SSE /DUSE_POPCNT /DUSE_SSE41 /DUSE_SSSE3 /DUSE_SSE2")
|
||||
set(SIMD_FLAGS "/arch:AVX2 /DUSE_POPCNT /arch:SSE /DUSE_SSE2 /DUSE_SSSE3 /DUSE_SSE41")
|
||||
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message(STATUS "Adding x86_64 specific flags")
|
||||
|
@ -25,9 +25,9 @@ if(MSVC)
|
|||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ox /DNDEBUG")
|
||||
|
||||
else()
|
||||
set(COMMON_FLAGS "-Wall -Wcast-qual -fno-exceptions -std=c++17 -pedantic -Wextra -Wshadow -Wmissing-declarations -flto -DUSE_PTHREADS")
|
||||
set(COMMON_FLAGS "-Wall -Wcast-qual -Wno-main -fno-exceptions -std=c++17 -pedantic -Wextra -Wshadow -Wmissing-declarations -flto -DUSE_PTHREADS")
|
||||
|
||||
set(SIMD_FLAGS "-msse -msse3 -mpopcnt -DUSE_POPCNT -DUSE_SSE41 -msse4.1 -DUSE_SSSE3 -mssse3 -DUSE_SSE2 -msse2")
|
||||
set(SIMD_FLAGS "-mpopcnt -DUSE_POPCNT -msse -DUSE_SSE2 -msse2 -msse3 -DUSE_SSSE3 -mssse3 -DUSE_SSE41 -msse4.1 -DUSE_SSE42 -msse4.2")
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
message(STATUS "Adding x86_64 specific flags")
|
||||
|
|
|
@ -15,9 +15,11 @@
|
|||
#include "fixes.h"
|
||||
#include "stockfish.h"
|
||||
|
||||
const char *QUITOK = "quitok\n";
|
||||
const char* QUITOK = "quitok\n";
|
||||
|
||||
int main(int, char **);
|
||||
void runMain() {}
|
||||
|
||||
int main(int, char**);
|
||||
|
||||
int stockfish_init() {
|
||||
fakein.open();
|
||||
|
@ -27,8 +29,11 @@ int stockfish_init() {
|
|||
|
||||
int stockfish_main() {
|
||||
int argc = 1;
|
||||
char *argv[] = {(char *)""};
|
||||
char* empty = (char*)malloc(0);
|
||||
*empty = 0;
|
||||
char* argv[] = {empty};
|
||||
int exitCode = main(argc, argv);
|
||||
free(empty);
|
||||
|
||||
fakeout << QUITOK << "\n";
|
||||
|
||||
|
@ -44,7 +49,7 @@ int stockfish_main() {
|
|||
return exitCode;
|
||||
}
|
||||
|
||||
ssize_t stockfish_stdin_write(char *data) {
|
||||
ssize_t stockfish_stdin_write(char* data) {
|
||||
std::string val(data);
|
||||
fakein << val << fakeendl;
|
||||
return val.length();
|
||||
|
@ -52,9 +57,15 @@ ssize_t stockfish_stdin_write(char *data) {
|
|||
|
||||
std::string data;
|
||||
|
||||
const char *stockfish_stdout_read() {
|
||||
if (getline(fakeout, data)) {
|
||||
return data.c_str();
|
||||
const char* stockfish_stdout_read(int trygetline) {
|
||||
if (trygetline) {
|
||||
if (fakeout.try_get_line(data)) {
|
||||
return data.c_str();
|
||||
}
|
||||
} else {
|
||||
if (getline(fakeout, data)) {
|
||||
return data.c_str();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
|
@ -20,29 +20,34 @@
|
|||
#ifdef _WIN32
|
||||
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define FFI_PLUGIN_EXPORT __attribute__((visibility("default"))) __attribute__((used))
|
||||
#define FFI_PLUGIN_EXPORT \
|
||||
__attribute__((visibility("default"))) __attribute__((used))
|
||||
#endif
|
||||
|
||||
// Initialisation of Stockfish.
|
||||
#ifndef _ffigen
|
||||
extern "C"
|
||||
#endif
|
||||
FFI_PLUGIN_EXPORT int stockfish_init();
|
||||
FFI_PLUGIN_EXPORT int
|
||||
stockfish_init();
|
||||
|
||||
// Stockfish main loop.
|
||||
#ifndef _ffigen
|
||||
extern "C"
|
||||
#endif
|
||||
FFI_PLUGIN_EXPORT int stockfish_main();
|
||||
FFI_PLUGIN_EXPORT int
|
||||
stockfish_main();
|
||||
|
||||
// Writing to Stockfish STDIN.
|
||||
#ifndef _ffigen
|
||||
extern "C"
|
||||
#endif
|
||||
FFI_PLUGIN_EXPORT ssize_t stockfish_stdin_write(char *data);
|
||||
FFI_PLUGIN_EXPORT ssize_t
|
||||
stockfish_stdin_write(char* data);
|
||||
|
||||
// Reading Stockfish STDOUT
|
||||
#ifndef _ffigen
|
||||
extern "C"
|
||||
#endif
|
||||
FFI_PLUGIN_EXPORT const char * stockfish_stdout_read();
|
||||
FFI_PLUGIN_EXPORT const char*
|
||||
stockfish_stdout_read(int trygetline);
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
bool FakeStream::try_get_line(std::string& val) {
|
||||
std::unique_lock<std::mutex> lock(mutex_guard);
|
||||
if (string_queue.empty() || closed) return false;
|
||||
if (string_queue.empty() || closed)
|
||||
return false;
|
||||
val = string_queue.front();
|
||||
string_queue.pop();
|
||||
return true;
|
||||
|
@ -20,18 +21,27 @@ void FakeStream::close() {
|
|||
while (!string_queue.empty()) {
|
||||
string_queue.pop();
|
||||
}
|
||||
mutex_signal.notify_one();
|
||||
mutex_signal.notify_all();
|
||||
}
|
||||
bool FakeStream::is_closed() {
|
||||
return closed;
|
||||
}
|
||||
bool FakeStream::is_closed() { return closed; }
|
||||
|
||||
std::streambuf* FakeStream::rdbuf() { return nullptr; }
|
||||
std::streambuf* FakeStream::rdbuf() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::streambuf* FakeStream::rdbuf(std::streambuf* buf) { return nullptr; }
|
||||
std::streambuf* FakeStream::rdbuf(std::streambuf* buf) {
|
||||
(void)buf;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool std::getline(FakeStream& is, std::string& str) {
|
||||
if (is.is_closed()) return false;
|
||||
if (is.is_closed())
|
||||
return false;
|
||||
is >> str;
|
||||
if (is.is_closed()) return false;
|
||||
if (is.is_closed())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,18 +6,24 @@
|
|||
#include <queue>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/threading.h>
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline std::string stringify(const T& input) {
|
||||
std::ostringstream output; // from www .ja va 2s . com
|
||||
output << input;
|
||||
return output.str();
|
||||
return std::string(output.str());
|
||||
}
|
||||
|
||||
class FakeStream {
|
||||
public:
|
||||
template <typename T>
|
||||
FakeStream& operator<<(const T& val) {
|
||||
if (closed) return *this;
|
||||
if (closed)
|
||||
return *this;
|
||||
std::lock_guard<std::mutex> lock(mutex_guard);
|
||||
string_queue.push(stringify(val));
|
||||
mutex_signal.notify_one();
|
||||
|
@ -25,29 +31,46 @@ class FakeStream {
|
|||
};
|
||||
template <typename T>
|
||||
FakeStream& operator>>(T& val) {
|
||||
if (closed) return *this;
|
||||
if (closed)
|
||||
return *this;
|
||||
std::unique_lock<std::mutex> lock(mutex_guard);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (emscripten_is_main_runtime_thread()) {
|
||||
lock.unlock();
|
||||
while (true) {
|
||||
lock = std::unique_lock<std::mutex>(mutex_guard);
|
||||
if (!string_queue.empty() || closed)
|
||||
break;
|
||||
lock.unlock();
|
||||
emscripten_sleep(10);
|
||||
}
|
||||
} else {
|
||||
mutex_signal.wait(
|
||||
lock, [this] { return !string_queue.empty() || closed; });
|
||||
}
|
||||
#else
|
||||
mutex_signal.wait(lock,
|
||||
[this] { return !string_queue.empty() || closed; });
|
||||
if (closed) return *this;
|
||||
#endif
|
||||
if (closed)
|
||||
return *this;
|
||||
val = string_queue.front();
|
||||
string_queue.pop();
|
||||
return *this;
|
||||
};
|
||||
|
||||
bool try_get_line(std::string& val);
|
||||
bool try_get_line(std::string&);
|
||||
|
||||
void open();
|
||||
void close();
|
||||
bool is_closed();
|
||||
|
||||
std::streambuf* rdbuf();
|
||||
std::streambuf* rdbuf(std::streambuf* __sb);
|
||||
std::streambuf* rdbuf(std::streambuf*);
|
||||
|
||||
private:
|
||||
bool closed = false;
|
||||
std::queue<std::string> string_queue;
|
||||
//std::string line;
|
||||
std::mutex mutex_guard;
|
||||
std::condition_variable mutex_signal;
|
||||
};
|
||||
|
@ -56,10 +79,6 @@ namespace std {
|
|||
bool getline(FakeStream& is, std::string& str);
|
||||
} // namespace std
|
||||
|
||||
// #define endl fakeendl
|
||||
// #define cout fakeout
|
||||
// #define cin fakein
|
||||
|
||||
extern FakeStream fakeout;
|
||||
extern FakeStream fakein;
|
||||
extern std::string fakeendl;
|
||||
|
|
Loading…
Reference in a new issue