From d67219935d9ddd4e7a6506f82067d88b84aaf2b9 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 19 Sep 2012 14:17:00 -0700 Subject: [PATCH 1/9] Move CFC one level down into clownfish/cfc. --- clownfish/{ => cfc}/include/CFC.h | 0 clownfish/{ => cfc}/perl/Build.PL | 0 clownfish/{ => cfc}/perl/MANIFEST | 0 .../perl/buildlib/Clownfish/CFC/Build.pm | 3 ++- clownfish/{ => cfc}/perl/lib/Clownfish.pod | 0 clownfish/{ => cfc}/perl/lib/Clownfish/CFC.pm | 0 clownfish/{ => cfc}/perl/lib/Clownfish/CFC.xs | 0 .../{ => cfc}/perl/lib/Clownfish/CFC/Base.pm | 0 .../perl/lib/Clownfish/CFC/Binding/Core.pm | 0 .../lib/Clownfish/CFC/Binding/Core/Aliases.pm | 0 .../lib/Clownfish/CFC/Binding/Core/Class.pm | 0 .../lib/Clownfish/CFC/Binding/Core/File.pm | 0 .../Clownfish/CFC/Binding/Core/Function.pm | 0 .../lib/Clownfish/CFC/Binding/Core/Method.pm | 0 .../perl/lib/Clownfish/CFC/Binding/Perl.pm | 0 .../lib/Clownfish/CFC/Binding/Perl/Class.pm | 0 .../Clownfish/CFC/Binding/Perl/Constructor.pm | 0 .../lib/Clownfish/CFC/Binding/Perl/Method.pm | 0 .../lib/Clownfish/CFC/Binding/Perl/Pod.pm | 0 .../Clownfish/CFC/Binding/Perl/Subroutine.pm | 0 .../lib/Clownfish/CFC/Binding/Perl/TypeMap.pm | 0 .../perl/lib/Clownfish/CFC/Dumpable.pm | 0 .../perl/lib/Clownfish/CFC/Model/CBlock.pm | 0 .../perl/lib/Clownfish/CFC/Model/Class.pm | 0 .../lib/Clownfish/CFC/Model/DocuComment.pm | 0 .../perl/lib/Clownfish/CFC/Model/File.pm | 0 .../perl/lib/Clownfish/CFC/Model/FileSpec.pm | 0 .../perl/lib/Clownfish/CFC/Model/Function.pm | 0 .../perl/lib/Clownfish/CFC/Model/Hierarchy.pm | 0 .../perl/lib/Clownfish/CFC/Model/Method.pm | 0 .../perl/lib/Clownfish/CFC/Model/ParamList.pm | 0 .../perl/lib/Clownfish/CFC/Model/Parcel.pm | 0 .../perl/lib/Clownfish/CFC/Model/Symbol.pm | 0 .../perl/lib/Clownfish/CFC/Model/Type.pm | 0 .../perl/lib/Clownfish/CFC/Model/Variable.pm | 0 .../perl/lib/Clownfish/CFC/Parser.pm | 0 .../perl/lib/Clownfish/CFC/Perl/Build.pm | 0 .../{ => cfc}/perl/lib/Clownfish/CFC/Util.pm | 0 clownfish/{ => cfc}/perl/t/000-load.t | 0 clownfish/{ => cfc}/perl/t/001-util.t | 0 clownfish/{ => cfc}/perl/t/050-docucomment.t | 0 clownfish/{ => cfc}/perl/t/051-symbol.t | 0 clownfish/{ => cfc}/perl/t/052-version.t | 0 clownfish/{ => cfc}/perl/t/100-type.t | 0 .../{ => cfc}/perl/t/101-primitive_type.t | 0 clownfish/{ => cfc}/perl/t/102-integer_type.t | 0 clownfish/{ => cfc}/perl/t/103-float_type.t | 0 clownfish/{ => cfc}/perl/t/104-void_type.t | 0 clownfish/{ => cfc}/perl/t/105-object_type.t | 0 clownfish/{ => cfc}/perl/t/106-va_list_type.t | 0 .../{ => cfc}/perl/t/107-arbitrary_type.t | 0 .../{ => cfc}/perl/t/108-composite_type.t | 0 clownfish/{ => cfc}/perl/t/200-function.t | 0 clownfish/{ => cfc}/perl/t/201-method.t | 0 .../{ => cfc}/perl/t/202-overridden_method.t | 0 clownfish/{ => cfc}/perl/t/203-final_method.t | 0 clownfish/{ => cfc}/perl/t/300-variable.t | 0 clownfish/{ => cfc}/perl/t/301-param_list.t | 0 clownfish/{ => cfc}/perl/t/400-file_spec.t | 0 clownfish/{ => cfc}/perl/t/401-class.t | 0 clownfish/{ => cfc}/perl/t/402-c_block.t | 0 clownfish/{ => cfc}/perl/t/403-parcel.t | 0 clownfish/{ => cfc}/perl/t/404-file.t | 0 clownfish/{ => cfc}/perl/t/500-hierarchy.t | 0 clownfish/{ => cfc}/perl/t/501-include_dir.t | 0 clownfish/{ => cfc}/perl/t/600-parser.t | 0 .../perl/t/cfclash/class/Animal/DogClash.cfh | 0 .../perl/t/cfclash/file/Animal/Dog.cfh | 0 .../perl/t/cfext/Animal/Rottweiler.cfh | 0 .../{ => cfc}/perl/t/cfsource/Animal.cfh | 0 .../{ => cfc}/perl/t/cfsource/Animal.cfp | 0 .../{ => cfc}/perl/t/cfsource/Animal/Dog.cfh | 0 .../{ => cfc}/perl/t/cfsource/Animal/Util.cfh | 0 clownfish/{ => cfc}/perl/typemap | 0 clownfish/{ => cfc}/ruby/Rakefile | 0 clownfish/{ => cfc}/ruby/ext/Clownfish/CFC.c | 0 .../{ => cfc}/ruby/ext/Clownfish/extconf.rb | 0 clownfish/{ => cfc}/src/CFCBase.c | 0 clownfish/{ => cfc}/src/CFCBase.h | 0 clownfish/{ => cfc}/src/CFCBindAliases.c | 0 clownfish/{ => cfc}/src/CFCBindAliases.h | 0 clownfish/{ => cfc}/src/CFCBindClass.c | 0 clownfish/{ => cfc}/src/CFCBindClass.h | 0 clownfish/{ => cfc}/src/CFCBindCore.c | 0 clownfish/{ => cfc}/src/CFCBindCore.h | 0 clownfish/{ => cfc}/src/CFCBindFile.c | 0 clownfish/{ => cfc}/src/CFCBindFile.h | 0 clownfish/{ => cfc}/src/CFCBindFunction.c | 0 clownfish/{ => cfc}/src/CFCBindFunction.h | 0 clownfish/{ => cfc}/src/CFCBindMethod.c | 0 clownfish/{ => cfc}/src/CFCBindMethod.h | 0 clownfish/{ => cfc}/src/CFCCBlock.c | 0 clownfish/{ => cfc}/src/CFCCBlock.h | 0 clownfish/{ => cfc}/src/CFCClass.c | 0 clownfish/{ => cfc}/src/CFCClass.h | 0 clownfish/{ => cfc}/src/CFCDocuComment.c | 0 clownfish/{ => cfc}/src/CFCDocuComment.h | 0 clownfish/{ => cfc}/src/CFCDumpable.c | 0 clownfish/{ => cfc}/src/CFCDumpable.h | 0 clownfish/{ => cfc}/src/CFCFile.c | 0 clownfish/{ => cfc}/src/CFCFile.h | 0 clownfish/{ => cfc}/src/CFCFileSpec.c | 0 clownfish/{ => cfc}/src/CFCFileSpec.h | 0 clownfish/{ => cfc}/src/CFCFunction.c | 0 clownfish/{ => cfc}/src/CFCFunction.h | 0 clownfish/{ => cfc}/src/CFCHierarchy.c | 0 clownfish/{ => cfc}/src/CFCHierarchy.h | 0 clownfish/{ => cfc}/src/CFCLexHeader.c | 0 clownfish/{ => cfc}/src/CFCLexHeader.h | 0 clownfish/{ => cfc}/src/CFCLexHeader.l | 0 clownfish/{ => cfc}/src/CFCMemPool.c | 0 clownfish/{ => cfc}/src/CFCMemPool.h | 0 clownfish/{ => cfc}/src/CFCMethod.c | 0 clownfish/{ => cfc}/src/CFCMethod.h | 0 clownfish/{ => cfc}/src/CFCParamList.c | 0 clownfish/{ => cfc}/src/CFCParamList.h | 0 clownfish/{ => cfc}/src/CFCParcel.c | 0 clownfish/{ => cfc}/src/CFCParcel.h | 0 clownfish/{ => cfc}/src/CFCParseHeader.y | 0 clownfish/{ => cfc}/src/CFCParser.c | 0 clownfish/{ => cfc}/src/CFCParser.h | 0 clownfish/{ => cfc}/src/CFCPerl.c | 0 clownfish/{ => cfc}/src/CFCPerl.h | 0 clownfish/{ => cfc}/src/CFCPerlClass.c | 0 clownfish/{ => cfc}/src/CFCPerlClass.h | 0 clownfish/{ => cfc}/src/CFCPerlConstructor.c | 0 clownfish/{ => cfc}/src/CFCPerlConstructor.h | 0 clownfish/{ => cfc}/src/CFCPerlMethod.c | 0 clownfish/{ => cfc}/src/CFCPerlMethod.h | 0 clownfish/{ => cfc}/src/CFCPerlPod.c | 0 clownfish/{ => cfc}/src/CFCPerlPod.h | 0 clownfish/{ => cfc}/src/CFCPerlSub.c | 0 clownfish/{ => cfc}/src/CFCPerlSub.h | 0 clownfish/{ => cfc}/src/CFCPerlTypeMap.c | 0 clownfish/{ => cfc}/src/CFCPerlTypeMap.h | 0 clownfish/{ => cfc}/src/CFCSymbol.c | 0 clownfish/{ => cfc}/src/CFCSymbol.h | 0 clownfish/{ => cfc}/src/CFCType.c | 0 clownfish/{ => cfc}/src/CFCType.h | 0 clownfish/{ => cfc}/src/CFCUtil.c | 0 clownfish/{ => cfc}/src/CFCUtil.h | 0 clownfish/{ => cfc}/src/CFCVariable.c | 0 clownfish/{ => cfc}/src/CFCVariable.h | 0 clownfish/{ => cfc}/src/CFCVersion.c | 0 clownfish/{ => cfc}/src/CFCVersion.h | 0 perl/buildlib/Lucy/Build.pm | 20 +++++++++---------- 146 files changed, 12 insertions(+), 11 deletions(-) rename clownfish/{ => cfc}/include/CFC.h (100%) rename clownfish/{ => cfc}/perl/Build.PL (100%) rename clownfish/{ => cfc}/perl/MANIFEST (100%) rename clownfish/{ => cfc}/perl/buildlib/Clownfish/CFC/Build.pm (98%) rename clownfish/{ => cfc}/perl/lib/Clownfish.pod (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC.xs (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Base.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Core.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Core/Aliases.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Core/Class.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Core/File.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Core/Function.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Core/Method.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Perl.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Perl/Class.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Perl/Constructor.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Perl/Method.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Perl/Pod.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Perl/Subroutine.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Binding/Perl/TypeMap.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Dumpable.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/CBlock.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/Class.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/DocuComment.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/File.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/FileSpec.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/Function.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/Hierarchy.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/Method.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/ParamList.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/Parcel.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/Symbol.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/Type.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Model/Variable.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Parser.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Perl/Build.pm (100%) rename clownfish/{ => cfc}/perl/lib/Clownfish/CFC/Util.pm (100%) rename clownfish/{ => cfc}/perl/t/000-load.t (100%) rename clownfish/{ => cfc}/perl/t/001-util.t (100%) rename clownfish/{ => cfc}/perl/t/050-docucomment.t (100%) rename clownfish/{ => cfc}/perl/t/051-symbol.t (100%) rename clownfish/{ => cfc}/perl/t/052-version.t (100%) rename clownfish/{ => cfc}/perl/t/100-type.t (100%) rename clownfish/{ => cfc}/perl/t/101-primitive_type.t (100%) rename clownfish/{ => cfc}/perl/t/102-integer_type.t (100%) rename clownfish/{ => cfc}/perl/t/103-float_type.t (100%) rename clownfish/{ => cfc}/perl/t/104-void_type.t (100%) rename clownfish/{ => cfc}/perl/t/105-object_type.t (100%) rename clownfish/{ => cfc}/perl/t/106-va_list_type.t (100%) rename clownfish/{ => cfc}/perl/t/107-arbitrary_type.t (100%) rename clownfish/{ => cfc}/perl/t/108-composite_type.t (100%) rename clownfish/{ => cfc}/perl/t/200-function.t (100%) rename clownfish/{ => cfc}/perl/t/201-method.t (100%) rename clownfish/{ => cfc}/perl/t/202-overridden_method.t (100%) rename clownfish/{ => cfc}/perl/t/203-final_method.t (100%) rename clownfish/{ => cfc}/perl/t/300-variable.t (100%) rename clownfish/{ => cfc}/perl/t/301-param_list.t (100%) rename clownfish/{ => cfc}/perl/t/400-file_spec.t (100%) rename clownfish/{ => cfc}/perl/t/401-class.t (100%) rename clownfish/{ => cfc}/perl/t/402-c_block.t (100%) rename clownfish/{ => cfc}/perl/t/403-parcel.t (100%) rename clownfish/{ => cfc}/perl/t/404-file.t (100%) rename clownfish/{ => cfc}/perl/t/500-hierarchy.t (100%) rename clownfish/{ => cfc}/perl/t/501-include_dir.t (100%) rename clownfish/{ => cfc}/perl/t/600-parser.t (100%) rename clownfish/{ => cfc}/perl/t/cfclash/class/Animal/DogClash.cfh (100%) rename clownfish/{ => cfc}/perl/t/cfclash/file/Animal/Dog.cfh (100%) rename clownfish/{ => cfc}/perl/t/cfext/Animal/Rottweiler.cfh (100%) rename clownfish/{ => cfc}/perl/t/cfsource/Animal.cfh (100%) rename clownfish/{ => cfc}/perl/t/cfsource/Animal.cfp (100%) rename clownfish/{ => cfc}/perl/t/cfsource/Animal/Dog.cfh (100%) rename clownfish/{ => cfc}/perl/t/cfsource/Animal/Util.cfh (100%) rename clownfish/{ => cfc}/perl/typemap (100%) rename clownfish/{ => cfc}/ruby/Rakefile (100%) rename clownfish/{ => cfc}/ruby/ext/Clownfish/CFC.c (100%) rename clownfish/{ => cfc}/ruby/ext/Clownfish/extconf.rb (100%) rename clownfish/{ => cfc}/src/CFCBase.c (100%) rename clownfish/{ => cfc}/src/CFCBase.h (100%) rename clownfish/{ => cfc}/src/CFCBindAliases.c (100%) rename clownfish/{ => cfc}/src/CFCBindAliases.h (100%) rename clownfish/{ => cfc}/src/CFCBindClass.c (100%) rename clownfish/{ => cfc}/src/CFCBindClass.h (100%) rename clownfish/{ => cfc}/src/CFCBindCore.c (100%) rename clownfish/{ => cfc}/src/CFCBindCore.h (100%) rename clownfish/{ => cfc}/src/CFCBindFile.c (100%) rename clownfish/{ => cfc}/src/CFCBindFile.h (100%) rename clownfish/{ => cfc}/src/CFCBindFunction.c (100%) rename clownfish/{ => cfc}/src/CFCBindFunction.h (100%) rename clownfish/{ => cfc}/src/CFCBindMethod.c (100%) rename clownfish/{ => cfc}/src/CFCBindMethod.h (100%) rename clownfish/{ => cfc}/src/CFCCBlock.c (100%) rename clownfish/{ => cfc}/src/CFCCBlock.h (100%) rename clownfish/{ => cfc}/src/CFCClass.c (100%) rename clownfish/{ => cfc}/src/CFCClass.h (100%) rename clownfish/{ => cfc}/src/CFCDocuComment.c (100%) rename clownfish/{ => cfc}/src/CFCDocuComment.h (100%) rename clownfish/{ => cfc}/src/CFCDumpable.c (100%) rename clownfish/{ => cfc}/src/CFCDumpable.h (100%) rename clownfish/{ => cfc}/src/CFCFile.c (100%) rename clownfish/{ => cfc}/src/CFCFile.h (100%) rename clownfish/{ => cfc}/src/CFCFileSpec.c (100%) rename clownfish/{ => cfc}/src/CFCFileSpec.h (100%) rename clownfish/{ => cfc}/src/CFCFunction.c (100%) rename clownfish/{ => cfc}/src/CFCFunction.h (100%) rename clownfish/{ => cfc}/src/CFCHierarchy.c (100%) rename clownfish/{ => cfc}/src/CFCHierarchy.h (100%) rename clownfish/{ => cfc}/src/CFCLexHeader.c (100%) rename clownfish/{ => cfc}/src/CFCLexHeader.h (100%) rename clownfish/{ => cfc}/src/CFCLexHeader.l (100%) rename clownfish/{ => cfc}/src/CFCMemPool.c (100%) rename clownfish/{ => cfc}/src/CFCMemPool.h (100%) rename clownfish/{ => cfc}/src/CFCMethod.c (100%) rename clownfish/{ => cfc}/src/CFCMethod.h (100%) rename clownfish/{ => cfc}/src/CFCParamList.c (100%) rename clownfish/{ => cfc}/src/CFCParamList.h (100%) rename clownfish/{ => cfc}/src/CFCParcel.c (100%) rename clownfish/{ => cfc}/src/CFCParcel.h (100%) rename clownfish/{ => cfc}/src/CFCParseHeader.y (100%) rename clownfish/{ => cfc}/src/CFCParser.c (100%) rename clownfish/{ => cfc}/src/CFCParser.h (100%) rename clownfish/{ => cfc}/src/CFCPerl.c (100%) rename clownfish/{ => cfc}/src/CFCPerl.h (100%) rename clownfish/{ => cfc}/src/CFCPerlClass.c (100%) rename clownfish/{ => cfc}/src/CFCPerlClass.h (100%) rename clownfish/{ => cfc}/src/CFCPerlConstructor.c (100%) rename clownfish/{ => cfc}/src/CFCPerlConstructor.h (100%) rename clownfish/{ => cfc}/src/CFCPerlMethod.c (100%) rename clownfish/{ => cfc}/src/CFCPerlMethod.h (100%) rename clownfish/{ => cfc}/src/CFCPerlPod.c (100%) rename clownfish/{ => cfc}/src/CFCPerlPod.h (100%) rename clownfish/{ => cfc}/src/CFCPerlSub.c (100%) rename clownfish/{ => cfc}/src/CFCPerlSub.h (100%) rename clownfish/{ => cfc}/src/CFCPerlTypeMap.c (100%) rename clownfish/{ => cfc}/src/CFCPerlTypeMap.h (100%) rename clownfish/{ => cfc}/src/CFCSymbol.c (100%) rename clownfish/{ => cfc}/src/CFCSymbol.h (100%) rename clownfish/{ => cfc}/src/CFCType.c (100%) rename clownfish/{ => cfc}/src/CFCType.h (100%) rename clownfish/{ => cfc}/src/CFCUtil.c (100%) rename clownfish/{ => cfc}/src/CFCUtil.h (100%) rename clownfish/{ => cfc}/src/CFCVariable.c (100%) rename clownfish/{ => cfc}/src/CFCVariable.h (100%) rename clownfish/{ => cfc}/src/CFCVersion.c (100%) rename clownfish/{ => cfc}/src/CFCVersion.h (100%) diff --git a/clownfish/include/CFC.h b/clownfish/cfc/include/CFC.h similarity index 100% rename from clownfish/include/CFC.h rename to clownfish/cfc/include/CFC.h diff --git a/clownfish/perl/Build.PL b/clownfish/cfc/perl/Build.PL similarity index 100% rename from clownfish/perl/Build.PL rename to clownfish/cfc/perl/Build.PL diff --git a/clownfish/perl/MANIFEST b/clownfish/cfc/perl/MANIFEST similarity index 100% rename from clownfish/perl/MANIFEST rename to clownfish/cfc/perl/MANIFEST diff --git a/clownfish/perl/buildlib/Clownfish/CFC/Build.pm b/clownfish/cfc/perl/buildlib/Clownfish/CFC/Build.pm similarity index 98% rename from clownfish/perl/buildlib/Clownfish/CFC/Build.pm rename to clownfish/cfc/perl/buildlib/Clownfish/CFC/Build.pm index 91ee99163..a29337737 100644 --- a/clownfish/perl/buildlib/Clownfish/CFC/Build.pm +++ b/clownfish/cfc/perl/buildlib/Clownfish/CFC/Build.pm @@ -22,8 +22,9 @@ use base qw( Module::Build ); use File::Spec::Functions qw( catfile updir catdir ); use Config; use Cwd qw( getcwd ); +use Carp; -my $base_dir = catdir( updir(), updir() ); +my $base_dir = catdir( updir(), updir(), updir() ); my $PPPORT_H_PATH = catfile( updir(), qw( include ppport.h ) ); my $LEMON_DIR = catdir( $base_dir, 'lemon' ); my $LEMON_EXE_PATH = catfile( $LEMON_DIR, "lemon$Config{_exe}" ); diff --git a/clownfish/perl/lib/Clownfish.pod b/clownfish/cfc/perl/lib/Clownfish.pod similarity index 100% rename from clownfish/perl/lib/Clownfish.pod rename to clownfish/cfc/perl/lib/Clownfish.pod diff --git a/clownfish/perl/lib/Clownfish/CFC.pm b/clownfish/cfc/perl/lib/Clownfish/CFC.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC.pm diff --git a/clownfish/perl/lib/Clownfish/CFC.xs b/clownfish/cfc/perl/lib/Clownfish/CFC.xs similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC.xs rename to clownfish/cfc/perl/lib/Clownfish/CFC.xs diff --git a/clownfish/perl/lib/Clownfish/CFC/Base.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Base.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Base.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Base.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Core.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Core.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Core/Aliases.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/Aliases.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Core/Aliases.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/Aliases.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Core/Class.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/Class.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Core/Class.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/Class.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Core/File.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/File.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Core/File.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/File.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Core/Function.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/Function.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Core/Function.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/Function.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Core/Method.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/Method.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Core/Method.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Core/Method.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Perl.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Perl.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Class.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Class.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Class.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Class.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Constructor.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Constructor.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Constructor.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Constructor.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Method.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Method.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Method.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Method.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Pod.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Pod.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Pod.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Pod.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Subroutine.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Subroutine.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Perl/Subroutine.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/Subroutine.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Binding/Perl/TypeMap.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/TypeMap.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Binding/Perl/TypeMap.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Binding/Perl/TypeMap.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Dumpable.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Dumpable.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Dumpable.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Dumpable.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/CBlock.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/CBlock.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/CBlock.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/CBlock.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/Class.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/Class.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/Class.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/Class.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/DocuComment.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/DocuComment.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/DocuComment.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/DocuComment.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/File.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/File.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/File.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/File.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/FileSpec.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/FileSpec.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/FileSpec.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/FileSpec.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/Function.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/Function.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/Function.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/Function.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/Hierarchy.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/Hierarchy.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/Hierarchy.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/Hierarchy.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/Method.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/Method.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/Method.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/Method.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/ParamList.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/ParamList.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/ParamList.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/ParamList.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/Parcel.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/Parcel.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/Parcel.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/Parcel.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/Symbol.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/Symbol.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/Symbol.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/Symbol.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/Type.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/Type.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/Type.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/Type.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Model/Variable.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Model/Variable.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Model/Variable.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Model/Variable.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Parser.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Parser.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Parser.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Parser.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Perl/Build.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Perl/Build.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Perl/Build.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Perl/Build.pm diff --git a/clownfish/perl/lib/Clownfish/CFC/Util.pm b/clownfish/cfc/perl/lib/Clownfish/CFC/Util.pm similarity index 100% rename from clownfish/perl/lib/Clownfish/CFC/Util.pm rename to clownfish/cfc/perl/lib/Clownfish/CFC/Util.pm diff --git a/clownfish/perl/t/000-load.t b/clownfish/cfc/perl/t/000-load.t similarity index 100% rename from clownfish/perl/t/000-load.t rename to clownfish/cfc/perl/t/000-load.t diff --git a/clownfish/perl/t/001-util.t b/clownfish/cfc/perl/t/001-util.t similarity index 100% rename from clownfish/perl/t/001-util.t rename to clownfish/cfc/perl/t/001-util.t diff --git a/clownfish/perl/t/050-docucomment.t b/clownfish/cfc/perl/t/050-docucomment.t similarity index 100% rename from clownfish/perl/t/050-docucomment.t rename to clownfish/cfc/perl/t/050-docucomment.t diff --git a/clownfish/perl/t/051-symbol.t b/clownfish/cfc/perl/t/051-symbol.t similarity index 100% rename from clownfish/perl/t/051-symbol.t rename to clownfish/cfc/perl/t/051-symbol.t diff --git a/clownfish/perl/t/052-version.t b/clownfish/cfc/perl/t/052-version.t similarity index 100% rename from clownfish/perl/t/052-version.t rename to clownfish/cfc/perl/t/052-version.t diff --git a/clownfish/perl/t/100-type.t b/clownfish/cfc/perl/t/100-type.t similarity index 100% rename from clownfish/perl/t/100-type.t rename to clownfish/cfc/perl/t/100-type.t diff --git a/clownfish/perl/t/101-primitive_type.t b/clownfish/cfc/perl/t/101-primitive_type.t similarity index 100% rename from clownfish/perl/t/101-primitive_type.t rename to clownfish/cfc/perl/t/101-primitive_type.t diff --git a/clownfish/perl/t/102-integer_type.t b/clownfish/cfc/perl/t/102-integer_type.t similarity index 100% rename from clownfish/perl/t/102-integer_type.t rename to clownfish/cfc/perl/t/102-integer_type.t diff --git a/clownfish/perl/t/103-float_type.t b/clownfish/cfc/perl/t/103-float_type.t similarity index 100% rename from clownfish/perl/t/103-float_type.t rename to clownfish/cfc/perl/t/103-float_type.t diff --git a/clownfish/perl/t/104-void_type.t b/clownfish/cfc/perl/t/104-void_type.t similarity index 100% rename from clownfish/perl/t/104-void_type.t rename to clownfish/cfc/perl/t/104-void_type.t diff --git a/clownfish/perl/t/105-object_type.t b/clownfish/cfc/perl/t/105-object_type.t similarity index 100% rename from clownfish/perl/t/105-object_type.t rename to clownfish/cfc/perl/t/105-object_type.t diff --git a/clownfish/perl/t/106-va_list_type.t b/clownfish/cfc/perl/t/106-va_list_type.t similarity index 100% rename from clownfish/perl/t/106-va_list_type.t rename to clownfish/cfc/perl/t/106-va_list_type.t diff --git a/clownfish/perl/t/107-arbitrary_type.t b/clownfish/cfc/perl/t/107-arbitrary_type.t similarity index 100% rename from clownfish/perl/t/107-arbitrary_type.t rename to clownfish/cfc/perl/t/107-arbitrary_type.t diff --git a/clownfish/perl/t/108-composite_type.t b/clownfish/cfc/perl/t/108-composite_type.t similarity index 100% rename from clownfish/perl/t/108-composite_type.t rename to clownfish/cfc/perl/t/108-composite_type.t diff --git a/clownfish/perl/t/200-function.t b/clownfish/cfc/perl/t/200-function.t similarity index 100% rename from clownfish/perl/t/200-function.t rename to clownfish/cfc/perl/t/200-function.t diff --git a/clownfish/perl/t/201-method.t b/clownfish/cfc/perl/t/201-method.t similarity index 100% rename from clownfish/perl/t/201-method.t rename to clownfish/cfc/perl/t/201-method.t diff --git a/clownfish/perl/t/202-overridden_method.t b/clownfish/cfc/perl/t/202-overridden_method.t similarity index 100% rename from clownfish/perl/t/202-overridden_method.t rename to clownfish/cfc/perl/t/202-overridden_method.t diff --git a/clownfish/perl/t/203-final_method.t b/clownfish/cfc/perl/t/203-final_method.t similarity index 100% rename from clownfish/perl/t/203-final_method.t rename to clownfish/cfc/perl/t/203-final_method.t diff --git a/clownfish/perl/t/300-variable.t b/clownfish/cfc/perl/t/300-variable.t similarity index 100% rename from clownfish/perl/t/300-variable.t rename to clownfish/cfc/perl/t/300-variable.t diff --git a/clownfish/perl/t/301-param_list.t b/clownfish/cfc/perl/t/301-param_list.t similarity index 100% rename from clownfish/perl/t/301-param_list.t rename to clownfish/cfc/perl/t/301-param_list.t diff --git a/clownfish/perl/t/400-file_spec.t b/clownfish/cfc/perl/t/400-file_spec.t similarity index 100% rename from clownfish/perl/t/400-file_spec.t rename to clownfish/cfc/perl/t/400-file_spec.t diff --git a/clownfish/perl/t/401-class.t b/clownfish/cfc/perl/t/401-class.t similarity index 100% rename from clownfish/perl/t/401-class.t rename to clownfish/cfc/perl/t/401-class.t diff --git a/clownfish/perl/t/402-c_block.t b/clownfish/cfc/perl/t/402-c_block.t similarity index 100% rename from clownfish/perl/t/402-c_block.t rename to clownfish/cfc/perl/t/402-c_block.t diff --git a/clownfish/perl/t/403-parcel.t b/clownfish/cfc/perl/t/403-parcel.t similarity index 100% rename from clownfish/perl/t/403-parcel.t rename to clownfish/cfc/perl/t/403-parcel.t diff --git a/clownfish/perl/t/404-file.t b/clownfish/cfc/perl/t/404-file.t similarity index 100% rename from clownfish/perl/t/404-file.t rename to clownfish/cfc/perl/t/404-file.t diff --git a/clownfish/perl/t/500-hierarchy.t b/clownfish/cfc/perl/t/500-hierarchy.t similarity index 100% rename from clownfish/perl/t/500-hierarchy.t rename to clownfish/cfc/perl/t/500-hierarchy.t diff --git a/clownfish/perl/t/501-include_dir.t b/clownfish/cfc/perl/t/501-include_dir.t similarity index 100% rename from clownfish/perl/t/501-include_dir.t rename to clownfish/cfc/perl/t/501-include_dir.t diff --git a/clownfish/perl/t/600-parser.t b/clownfish/cfc/perl/t/600-parser.t similarity index 100% rename from clownfish/perl/t/600-parser.t rename to clownfish/cfc/perl/t/600-parser.t diff --git a/clownfish/perl/t/cfclash/class/Animal/DogClash.cfh b/clownfish/cfc/perl/t/cfclash/class/Animal/DogClash.cfh similarity index 100% rename from clownfish/perl/t/cfclash/class/Animal/DogClash.cfh rename to clownfish/cfc/perl/t/cfclash/class/Animal/DogClash.cfh diff --git a/clownfish/perl/t/cfclash/file/Animal/Dog.cfh b/clownfish/cfc/perl/t/cfclash/file/Animal/Dog.cfh similarity index 100% rename from clownfish/perl/t/cfclash/file/Animal/Dog.cfh rename to clownfish/cfc/perl/t/cfclash/file/Animal/Dog.cfh diff --git a/clownfish/perl/t/cfext/Animal/Rottweiler.cfh b/clownfish/cfc/perl/t/cfext/Animal/Rottweiler.cfh similarity index 100% rename from clownfish/perl/t/cfext/Animal/Rottweiler.cfh rename to clownfish/cfc/perl/t/cfext/Animal/Rottweiler.cfh diff --git a/clownfish/perl/t/cfsource/Animal.cfh b/clownfish/cfc/perl/t/cfsource/Animal.cfh similarity index 100% rename from clownfish/perl/t/cfsource/Animal.cfh rename to clownfish/cfc/perl/t/cfsource/Animal.cfh diff --git a/clownfish/perl/t/cfsource/Animal.cfp b/clownfish/cfc/perl/t/cfsource/Animal.cfp similarity index 100% rename from clownfish/perl/t/cfsource/Animal.cfp rename to clownfish/cfc/perl/t/cfsource/Animal.cfp diff --git a/clownfish/perl/t/cfsource/Animal/Dog.cfh b/clownfish/cfc/perl/t/cfsource/Animal/Dog.cfh similarity index 100% rename from clownfish/perl/t/cfsource/Animal/Dog.cfh rename to clownfish/cfc/perl/t/cfsource/Animal/Dog.cfh diff --git a/clownfish/perl/t/cfsource/Animal/Util.cfh b/clownfish/cfc/perl/t/cfsource/Animal/Util.cfh similarity index 100% rename from clownfish/perl/t/cfsource/Animal/Util.cfh rename to clownfish/cfc/perl/t/cfsource/Animal/Util.cfh diff --git a/clownfish/perl/typemap b/clownfish/cfc/perl/typemap similarity index 100% rename from clownfish/perl/typemap rename to clownfish/cfc/perl/typemap diff --git a/clownfish/ruby/Rakefile b/clownfish/cfc/ruby/Rakefile similarity index 100% rename from clownfish/ruby/Rakefile rename to clownfish/cfc/ruby/Rakefile diff --git a/clownfish/ruby/ext/Clownfish/CFC.c b/clownfish/cfc/ruby/ext/Clownfish/CFC.c similarity index 100% rename from clownfish/ruby/ext/Clownfish/CFC.c rename to clownfish/cfc/ruby/ext/Clownfish/CFC.c diff --git a/clownfish/ruby/ext/Clownfish/extconf.rb b/clownfish/cfc/ruby/ext/Clownfish/extconf.rb similarity index 100% rename from clownfish/ruby/ext/Clownfish/extconf.rb rename to clownfish/cfc/ruby/ext/Clownfish/extconf.rb diff --git a/clownfish/src/CFCBase.c b/clownfish/cfc/src/CFCBase.c similarity index 100% rename from clownfish/src/CFCBase.c rename to clownfish/cfc/src/CFCBase.c diff --git a/clownfish/src/CFCBase.h b/clownfish/cfc/src/CFCBase.h similarity index 100% rename from clownfish/src/CFCBase.h rename to clownfish/cfc/src/CFCBase.h diff --git a/clownfish/src/CFCBindAliases.c b/clownfish/cfc/src/CFCBindAliases.c similarity index 100% rename from clownfish/src/CFCBindAliases.c rename to clownfish/cfc/src/CFCBindAliases.c diff --git a/clownfish/src/CFCBindAliases.h b/clownfish/cfc/src/CFCBindAliases.h similarity index 100% rename from clownfish/src/CFCBindAliases.h rename to clownfish/cfc/src/CFCBindAliases.h diff --git a/clownfish/src/CFCBindClass.c b/clownfish/cfc/src/CFCBindClass.c similarity index 100% rename from clownfish/src/CFCBindClass.c rename to clownfish/cfc/src/CFCBindClass.c diff --git a/clownfish/src/CFCBindClass.h b/clownfish/cfc/src/CFCBindClass.h similarity index 100% rename from clownfish/src/CFCBindClass.h rename to clownfish/cfc/src/CFCBindClass.h diff --git a/clownfish/src/CFCBindCore.c b/clownfish/cfc/src/CFCBindCore.c similarity index 100% rename from clownfish/src/CFCBindCore.c rename to clownfish/cfc/src/CFCBindCore.c diff --git a/clownfish/src/CFCBindCore.h b/clownfish/cfc/src/CFCBindCore.h similarity index 100% rename from clownfish/src/CFCBindCore.h rename to clownfish/cfc/src/CFCBindCore.h diff --git a/clownfish/src/CFCBindFile.c b/clownfish/cfc/src/CFCBindFile.c similarity index 100% rename from clownfish/src/CFCBindFile.c rename to clownfish/cfc/src/CFCBindFile.c diff --git a/clownfish/src/CFCBindFile.h b/clownfish/cfc/src/CFCBindFile.h similarity index 100% rename from clownfish/src/CFCBindFile.h rename to clownfish/cfc/src/CFCBindFile.h diff --git a/clownfish/src/CFCBindFunction.c b/clownfish/cfc/src/CFCBindFunction.c similarity index 100% rename from clownfish/src/CFCBindFunction.c rename to clownfish/cfc/src/CFCBindFunction.c diff --git a/clownfish/src/CFCBindFunction.h b/clownfish/cfc/src/CFCBindFunction.h similarity index 100% rename from clownfish/src/CFCBindFunction.h rename to clownfish/cfc/src/CFCBindFunction.h diff --git a/clownfish/src/CFCBindMethod.c b/clownfish/cfc/src/CFCBindMethod.c similarity index 100% rename from clownfish/src/CFCBindMethod.c rename to clownfish/cfc/src/CFCBindMethod.c diff --git a/clownfish/src/CFCBindMethod.h b/clownfish/cfc/src/CFCBindMethod.h similarity index 100% rename from clownfish/src/CFCBindMethod.h rename to clownfish/cfc/src/CFCBindMethod.h diff --git a/clownfish/src/CFCCBlock.c b/clownfish/cfc/src/CFCCBlock.c similarity index 100% rename from clownfish/src/CFCCBlock.c rename to clownfish/cfc/src/CFCCBlock.c diff --git a/clownfish/src/CFCCBlock.h b/clownfish/cfc/src/CFCCBlock.h similarity index 100% rename from clownfish/src/CFCCBlock.h rename to clownfish/cfc/src/CFCCBlock.h diff --git a/clownfish/src/CFCClass.c b/clownfish/cfc/src/CFCClass.c similarity index 100% rename from clownfish/src/CFCClass.c rename to clownfish/cfc/src/CFCClass.c diff --git a/clownfish/src/CFCClass.h b/clownfish/cfc/src/CFCClass.h similarity index 100% rename from clownfish/src/CFCClass.h rename to clownfish/cfc/src/CFCClass.h diff --git a/clownfish/src/CFCDocuComment.c b/clownfish/cfc/src/CFCDocuComment.c similarity index 100% rename from clownfish/src/CFCDocuComment.c rename to clownfish/cfc/src/CFCDocuComment.c diff --git a/clownfish/src/CFCDocuComment.h b/clownfish/cfc/src/CFCDocuComment.h similarity index 100% rename from clownfish/src/CFCDocuComment.h rename to clownfish/cfc/src/CFCDocuComment.h diff --git a/clownfish/src/CFCDumpable.c b/clownfish/cfc/src/CFCDumpable.c similarity index 100% rename from clownfish/src/CFCDumpable.c rename to clownfish/cfc/src/CFCDumpable.c diff --git a/clownfish/src/CFCDumpable.h b/clownfish/cfc/src/CFCDumpable.h similarity index 100% rename from clownfish/src/CFCDumpable.h rename to clownfish/cfc/src/CFCDumpable.h diff --git a/clownfish/src/CFCFile.c b/clownfish/cfc/src/CFCFile.c similarity index 100% rename from clownfish/src/CFCFile.c rename to clownfish/cfc/src/CFCFile.c diff --git a/clownfish/src/CFCFile.h b/clownfish/cfc/src/CFCFile.h similarity index 100% rename from clownfish/src/CFCFile.h rename to clownfish/cfc/src/CFCFile.h diff --git a/clownfish/src/CFCFileSpec.c b/clownfish/cfc/src/CFCFileSpec.c similarity index 100% rename from clownfish/src/CFCFileSpec.c rename to clownfish/cfc/src/CFCFileSpec.c diff --git a/clownfish/src/CFCFileSpec.h b/clownfish/cfc/src/CFCFileSpec.h similarity index 100% rename from clownfish/src/CFCFileSpec.h rename to clownfish/cfc/src/CFCFileSpec.h diff --git a/clownfish/src/CFCFunction.c b/clownfish/cfc/src/CFCFunction.c similarity index 100% rename from clownfish/src/CFCFunction.c rename to clownfish/cfc/src/CFCFunction.c diff --git a/clownfish/src/CFCFunction.h b/clownfish/cfc/src/CFCFunction.h similarity index 100% rename from clownfish/src/CFCFunction.h rename to clownfish/cfc/src/CFCFunction.h diff --git a/clownfish/src/CFCHierarchy.c b/clownfish/cfc/src/CFCHierarchy.c similarity index 100% rename from clownfish/src/CFCHierarchy.c rename to clownfish/cfc/src/CFCHierarchy.c diff --git a/clownfish/src/CFCHierarchy.h b/clownfish/cfc/src/CFCHierarchy.h similarity index 100% rename from clownfish/src/CFCHierarchy.h rename to clownfish/cfc/src/CFCHierarchy.h diff --git a/clownfish/src/CFCLexHeader.c b/clownfish/cfc/src/CFCLexHeader.c similarity index 100% rename from clownfish/src/CFCLexHeader.c rename to clownfish/cfc/src/CFCLexHeader.c diff --git a/clownfish/src/CFCLexHeader.h b/clownfish/cfc/src/CFCLexHeader.h similarity index 100% rename from clownfish/src/CFCLexHeader.h rename to clownfish/cfc/src/CFCLexHeader.h diff --git a/clownfish/src/CFCLexHeader.l b/clownfish/cfc/src/CFCLexHeader.l similarity index 100% rename from clownfish/src/CFCLexHeader.l rename to clownfish/cfc/src/CFCLexHeader.l diff --git a/clownfish/src/CFCMemPool.c b/clownfish/cfc/src/CFCMemPool.c similarity index 100% rename from clownfish/src/CFCMemPool.c rename to clownfish/cfc/src/CFCMemPool.c diff --git a/clownfish/src/CFCMemPool.h b/clownfish/cfc/src/CFCMemPool.h similarity index 100% rename from clownfish/src/CFCMemPool.h rename to clownfish/cfc/src/CFCMemPool.h diff --git a/clownfish/src/CFCMethod.c b/clownfish/cfc/src/CFCMethod.c similarity index 100% rename from clownfish/src/CFCMethod.c rename to clownfish/cfc/src/CFCMethod.c diff --git a/clownfish/src/CFCMethod.h b/clownfish/cfc/src/CFCMethod.h similarity index 100% rename from clownfish/src/CFCMethod.h rename to clownfish/cfc/src/CFCMethod.h diff --git a/clownfish/src/CFCParamList.c b/clownfish/cfc/src/CFCParamList.c similarity index 100% rename from clownfish/src/CFCParamList.c rename to clownfish/cfc/src/CFCParamList.c diff --git a/clownfish/src/CFCParamList.h b/clownfish/cfc/src/CFCParamList.h similarity index 100% rename from clownfish/src/CFCParamList.h rename to clownfish/cfc/src/CFCParamList.h diff --git a/clownfish/src/CFCParcel.c b/clownfish/cfc/src/CFCParcel.c similarity index 100% rename from clownfish/src/CFCParcel.c rename to clownfish/cfc/src/CFCParcel.c diff --git a/clownfish/src/CFCParcel.h b/clownfish/cfc/src/CFCParcel.h similarity index 100% rename from clownfish/src/CFCParcel.h rename to clownfish/cfc/src/CFCParcel.h diff --git a/clownfish/src/CFCParseHeader.y b/clownfish/cfc/src/CFCParseHeader.y similarity index 100% rename from clownfish/src/CFCParseHeader.y rename to clownfish/cfc/src/CFCParseHeader.y diff --git a/clownfish/src/CFCParser.c b/clownfish/cfc/src/CFCParser.c similarity index 100% rename from clownfish/src/CFCParser.c rename to clownfish/cfc/src/CFCParser.c diff --git a/clownfish/src/CFCParser.h b/clownfish/cfc/src/CFCParser.h similarity index 100% rename from clownfish/src/CFCParser.h rename to clownfish/cfc/src/CFCParser.h diff --git a/clownfish/src/CFCPerl.c b/clownfish/cfc/src/CFCPerl.c similarity index 100% rename from clownfish/src/CFCPerl.c rename to clownfish/cfc/src/CFCPerl.c diff --git a/clownfish/src/CFCPerl.h b/clownfish/cfc/src/CFCPerl.h similarity index 100% rename from clownfish/src/CFCPerl.h rename to clownfish/cfc/src/CFCPerl.h diff --git a/clownfish/src/CFCPerlClass.c b/clownfish/cfc/src/CFCPerlClass.c similarity index 100% rename from clownfish/src/CFCPerlClass.c rename to clownfish/cfc/src/CFCPerlClass.c diff --git a/clownfish/src/CFCPerlClass.h b/clownfish/cfc/src/CFCPerlClass.h similarity index 100% rename from clownfish/src/CFCPerlClass.h rename to clownfish/cfc/src/CFCPerlClass.h diff --git a/clownfish/src/CFCPerlConstructor.c b/clownfish/cfc/src/CFCPerlConstructor.c similarity index 100% rename from clownfish/src/CFCPerlConstructor.c rename to clownfish/cfc/src/CFCPerlConstructor.c diff --git a/clownfish/src/CFCPerlConstructor.h b/clownfish/cfc/src/CFCPerlConstructor.h similarity index 100% rename from clownfish/src/CFCPerlConstructor.h rename to clownfish/cfc/src/CFCPerlConstructor.h diff --git a/clownfish/src/CFCPerlMethod.c b/clownfish/cfc/src/CFCPerlMethod.c similarity index 100% rename from clownfish/src/CFCPerlMethod.c rename to clownfish/cfc/src/CFCPerlMethod.c diff --git a/clownfish/src/CFCPerlMethod.h b/clownfish/cfc/src/CFCPerlMethod.h similarity index 100% rename from clownfish/src/CFCPerlMethod.h rename to clownfish/cfc/src/CFCPerlMethod.h diff --git a/clownfish/src/CFCPerlPod.c b/clownfish/cfc/src/CFCPerlPod.c similarity index 100% rename from clownfish/src/CFCPerlPod.c rename to clownfish/cfc/src/CFCPerlPod.c diff --git a/clownfish/src/CFCPerlPod.h b/clownfish/cfc/src/CFCPerlPod.h similarity index 100% rename from clownfish/src/CFCPerlPod.h rename to clownfish/cfc/src/CFCPerlPod.h diff --git a/clownfish/src/CFCPerlSub.c b/clownfish/cfc/src/CFCPerlSub.c similarity index 100% rename from clownfish/src/CFCPerlSub.c rename to clownfish/cfc/src/CFCPerlSub.c diff --git a/clownfish/src/CFCPerlSub.h b/clownfish/cfc/src/CFCPerlSub.h similarity index 100% rename from clownfish/src/CFCPerlSub.h rename to clownfish/cfc/src/CFCPerlSub.h diff --git a/clownfish/src/CFCPerlTypeMap.c b/clownfish/cfc/src/CFCPerlTypeMap.c similarity index 100% rename from clownfish/src/CFCPerlTypeMap.c rename to clownfish/cfc/src/CFCPerlTypeMap.c diff --git a/clownfish/src/CFCPerlTypeMap.h b/clownfish/cfc/src/CFCPerlTypeMap.h similarity index 100% rename from clownfish/src/CFCPerlTypeMap.h rename to clownfish/cfc/src/CFCPerlTypeMap.h diff --git a/clownfish/src/CFCSymbol.c b/clownfish/cfc/src/CFCSymbol.c similarity index 100% rename from clownfish/src/CFCSymbol.c rename to clownfish/cfc/src/CFCSymbol.c diff --git a/clownfish/src/CFCSymbol.h b/clownfish/cfc/src/CFCSymbol.h similarity index 100% rename from clownfish/src/CFCSymbol.h rename to clownfish/cfc/src/CFCSymbol.h diff --git a/clownfish/src/CFCType.c b/clownfish/cfc/src/CFCType.c similarity index 100% rename from clownfish/src/CFCType.c rename to clownfish/cfc/src/CFCType.c diff --git a/clownfish/src/CFCType.h b/clownfish/cfc/src/CFCType.h similarity index 100% rename from clownfish/src/CFCType.h rename to clownfish/cfc/src/CFCType.h diff --git a/clownfish/src/CFCUtil.c b/clownfish/cfc/src/CFCUtil.c similarity index 100% rename from clownfish/src/CFCUtil.c rename to clownfish/cfc/src/CFCUtil.c diff --git a/clownfish/src/CFCUtil.h b/clownfish/cfc/src/CFCUtil.h similarity index 100% rename from clownfish/src/CFCUtil.h rename to clownfish/cfc/src/CFCUtil.h diff --git a/clownfish/src/CFCVariable.c b/clownfish/cfc/src/CFCVariable.c similarity index 100% rename from clownfish/src/CFCVariable.c rename to clownfish/cfc/src/CFCVariable.c diff --git a/clownfish/src/CFCVariable.h b/clownfish/cfc/src/CFCVariable.h similarity index 100% rename from clownfish/src/CFCVariable.h rename to clownfish/cfc/src/CFCVariable.h diff --git a/clownfish/src/CFCVersion.c b/clownfish/cfc/src/CFCVersion.c similarity index 100% rename from clownfish/src/CFCVersion.c rename to clownfish/cfc/src/CFCVersion.c diff --git a/clownfish/src/CFCVersion.h b/clownfish/cfc/src/CFCVersion.h similarity index 100% rename from clownfish/src/CFCVersion.h rename to clownfish/cfc/src/CFCVersion.h diff --git a/perl/buildlib/Lucy/Build.pm b/perl/buildlib/Lucy/Build.pm index 1cbcd66f7..006ffba29 100644 --- a/perl/buildlib/Lucy/Build.pm +++ b/perl/buildlib/Lucy/Build.pm @@ -16,21 +16,21 @@ use strict; use warnings; -use lib '../clownfish/perl/blib/arch'; -use lib '../clownfish/perl/blib/lib'; -use lib 'clownfish/perl/blib/arch'; -use lib 'clownfish/perl/blib/lib'; +use lib '../clownfish/cfc/perl/blib/arch'; +use lib '../clownfish/cfc/perl/blib/lib'; +use lib 'clownfish/cfc/perl/blib/arch'; +use lib 'clownfish/cfc/perl/blib/lib'; package Lucy::Build; # We want to subclass Clownfish::CFC::Perl::Build, but Clownfish might not be -# built yet. So we look in 'clownfish/perl/lib' directly and cleanup @INC +# built yet. So we look in 'clownfish/cfc/perl/lib' directly and cleanup @INC # afterwards. -use lib '../clownfish/perl/lib'; -use lib 'clownfish/perl/lib'; +use lib '../clownfish/cfc/perl/lib'; +use lib 'clownfish/cfc/perl/lib'; use base qw( Clownfish::CFC::Perl::Build ); -no lib '../clownfish/perl/lib'; -no lib 'clownfish/perl/lib'; +no lib '../clownfish/cfc/perl/lib'; +no lib 'clownfish/cfc/perl/lib'; our $VERSION = '0.003000'; $VERSION = eval $VERSION; @@ -54,7 +54,7 @@ my $CHARMONY_PM_PATH = 'Charmony.pm'; my $LEMON_DIR = catdir( @BASE_PATH, 'lemon' ); my $LEMON_EXE_PATH = catfile( $LEMON_DIR, "lemon$Config{_exe}" ); my $CORE_SOURCE_DIR = catdir( @BASE_PATH, 'core' ); -my $CLOWNFISH_DIR = catdir( @BASE_PATH, 'clownfish', 'perl' ); +my $CLOWNFISH_DIR = catdir( @BASE_PATH, 'clownfish', 'cfc', 'perl' ); my $CLOWNFISH_BUILD = catfile( $CLOWNFISH_DIR, 'Build' ); my $LIB_DIR = 'lib'; From cc0ad416395890859ca5097248f220741dfa7ed3 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 19 Sep 2012 14:31:53 -0700 Subject: [PATCH 2/9] Copy Clownfish files into place. Copy source files for Clownfish core classes out of Lucy directories. No build yet, as the files are copied verbatim. --- clownfish/core/Clownfish/ByteBuf.c | 265 +++++ clownfish/core/Clownfish/ByteBuf.cfh | 159 +++ clownfish/core/Clownfish/CharBuf.c | 979 ++++++++++++++++++ clownfish/core/Clownfish/CharBuf.cfh | 405 ++++++++ clownfish/core/Clownfish/Err.c | 261 +++++ clownfish/core/Clownfish/Err.cfh | 258 +++++ clownfish/core/Clownfish/Hash.c | 500 +++++++++ clownfish/core/Clownfish/Hash.cfh | 165 +++ clownfish/core/Clownfish/Host.cfh | 108 ++ clownfish/core/Clownfish/LockFreeRegistry.c | 129 +++ clownfish/core/Clownfish/LockFreeRegistry.cfh | 45 + clownfish/core/Clownfish/Method.c | 64 ++ clownfish/core/Clownfish/Method.cfh | 48 + clownfish/core/Clownfish/Num.c | 456 ++++++++ clownfish/core/Clownfish/Num.cfh | 307 ++++++ clownfish/core/Clownfish/Obj.c | 125 +++ clownfish/core/Clownfish/Obj.cfh | 225 ++++ clownfish/core/Clownfish/Util/Atomic.c | 40 + clownfish/core/Clownfish/Util/Atomic.cfh | 109 ++ clownfish/core/Clownfish/Util/Memory.c | 112 ++ clownfish/core/Clownfish/Util/Memory.cfh | 73 ++ clownfish/core/Clownfish/Util/NumberUtils.c | 35 + clownfish/core/Clownfish/Util/NumberUtils.cfh | 474 +++++++++ clownfish/core/Clownfish/Util/SortUtils.c | 462 +++++++++ clownfish/core/Clownfish/Util/SortUtils.cfh | 68 ++ clownfish/core/Clownfish/Util/StringHelper.c | 229 ++++ .../core/Clownfish/Util/StringHelper.cfh | 83 ++ clownfish/core/Clownfish/VArray.c | 370 +++++++ clownfish/core/Clownfish/VArray.cfh | 181 ++++ clownfish/core/Clownfish/VTable.c | 330 ++++++ clownfish/core/Clownfish/VTable.cfh | 157 +++ clownfish/perl/Build.PL | 84 ++ clownfish/perl/MANIFEST | 7 + clownfish/perl/buildlib/Clownfish/Build.pm | 550 ++++++++++ .../perl/buildlib/Clownfish/Build/Binding.pm | 881 ++++++++++++++++ clownfish/perl/lib/Clownfish.pm | 502 +++++++++ clownfish/perl/lib/Clownfish/ByteBuf.pm | 25 + clownfish/perl/lib/Clownfish/CharBuf.pm | 25 + clownfish/perl/lib/Clownfish/Err.pm | 25 + clownfish/perl/lib/Clownfish/Hash.pm | 25 + clownfish/perl/lib/Clownfish/Host.pm | 25 + .../perl/lib/Clownfish/LockFreeRegistry.pm | 25 + clownfish/perl/lib/Clownfish/Num.pm | 25 + clownfish/perl/lib/Clownfish/Obj.pm | 25 + clownfish/perl/lib/Clownfish/VArray.pm | 25 + clownfish/perl/lib/Clownfish/VTable.pm | 25 + clownfish/perl/xs/Clownfish/Err.c | 146 +++ clownfish/perl/xs/Clownfish/Host.c | 246 +++++ .../perl/xs/Clownfish/LockFreeRegistry.c | 36 + clownfish/perl/xs/Clownfish/Obj.c | 113 ++ clownfish/perl/xs/Clownfish/VTable.c | 70 ++ clownfish/perl/xs/XSBind.c | 582 +++++++++++ clownfish/perl/xs/XSBind.h | 349 +++++++ 53 files changed, 11038 insertions(+) create mode 100644 clownfish/core/Clownfish/ByteBuf.c create mode 100644 clownfish/core/Clownfish/ByteBuf.cfh create mode 100644 clownfish/core/Clownfish/CharBuf.c create mode 100644 clownfish/core/Clownfish/CharBuf.cfh create mode 100644 clownfish/core/Clownfish/Err.c create mode 100644 clownfish/core/Clownfish/Err.cfh create mode 100644 clownfish/core/Clownfish/Hash.c create mode 100644 clownfish/core/Clownfish/Hash.cfh create mode 100644 clownfish/core/Clownfish/Host.cfh create mode 100644 clownfish/core/Clownfish/LockFreeRegistry.c create mode 100644 clownfish/core/Clownfish/LockFreeRegistry.cfh create mode 100644 clownfish/core/Clownfish/Method.c create mode 100644 clownfish/core/Clownfish/Method.cfh create mode 100644 clownfish/core/Clownfish/Num.c create mode 100644 clownfish/core/Clownfish/Num.cfh create mode 100644 clownfish/core/Clownfish/Obj.c create mode 100644 clownfish/core/Clownfish/Obj.cfh create mode 100644 clownfish/core/Clownfish/Util/Atomic.c create mode 100644 clownfish/core/Clownfish/Util/Atomic.cfh create mode 100644 clownfish/core/Clownfish/Util/Memory.c create mode 100644 clownfish/core/Clownfish/Util/Memory.cfh create mode 100644 clownfish/core/Clownfish/Util/NumberUtils.c create mode 100644 clownfish/core/Clownfish/Util/NumberUtils.cfh create mode 100644 clownfish/core/Clownfish/Util/SortUtils.c create mode 100644 clownfish/core/Clownfish/Util/SortUtils.cfh create mode 100644 clownfish/core/Clownfish/Util/StringHelper.c create mode 100644 clownfish/core/Clownfish/Util/StringHelper.cfh create mode 100644 clownfish/core/Clownfish/VArray.c create mode 100644 clownfish/core/Clownfish/VArray.cfh create mode 100644 clownfish/core/Clownfish/VTable.c create mode 100644 clownfish/core/Clownfish/VTable.cfh create mode 100644 clownfish/perl/Build.PL create mode 100644 clownfish/perl/MANIFEST create mode 100644 clownfish/perl/buildlib/Clownfish/Build.pm create mode 100644 clownfish/perl/buildlib/Clownfish/Build/Binding.pm create mode 100644 clownfish/perl/lib/Clownfish.pm create mode 100644 clownfish/perl/lib/Clownfish/ByteBuf.pm create mode 100644 clownfish/perl/lib/Clownfish/CharBuf.pm create mode 100644 clownfish/perl/lib/Clownfish/Err.pm create mode 100644 clownfish/perl/lib/Clownfish/Hash.pm create mode 100644 clownfish/perl/lib/Clownfish/Host.pm create mode 100644 clownfish/perl/lib/Clownfish/LockFreeRegistry.pm create mode 100644 clownfish/perl/lib/Clownfish/Num.pm create mode 100644 clownfish/perl/lib/Clownfish/Obj.pm create mode 100644 clownfish/perl/lib/Clownfish/VArray.pm create mode 100644 clownfish/perl/lib/Clownfish/VTable.pm create mode 100644 clownfish/perl/xs/Clownfish/Err.c create mode 100644 clownfish/perl/xs/Clownfish/Host.c create mode 100644 clownfish/perl/xs/Clownfish/LockFreeRegistry.c create mode 100644 clownfish/perl/xs/Clownfish/Obj.c create mode 100644 clownfish/perl/xs/Clownfish/VTable.c create mode 100644 clownfish/perl/xs/XSBind.c create mode 100644 clownfish/perl/xs/XSBind.h diff --git a/clownfish/core/Clownfish/ByteBuf.c b/clownfish/core/Clownfish/ByteBuf.c new file mode 100644 index 000000000..776266843 --- /dev/null +++ b/clownfish/core/Clownfish/ByteBuf.c @@ -0,0 +1,265 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_BYTEBUF +#define C_LUCY_VIEWBYTEBUF +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include +#include +#include +#include + +#include "Clownfish/VTable.h" +#include "Clownfish/ByteBuf.h" +#include "Clownfish/Err.h" +#include "Lucy/Store/InStream.h" +#include "Lucy/Store/OutStream.h" +#include "Clownfish/Util/Memory.h" + +static void +S_grow(ByteBuf *self, size_t size); + +ByteBuf* +BB_new(size_t capacity) { + ByteBuf *self = (ByteBuf*)VTable_Make_Obj(BYTEBUF); + return BB_init(self, capacity); +} + +ByteBuf* +BB_init(ByteBuf *self, size_t capacity) { + size_t amount = capacity ? capacity : sizeof(int64_t); + self->buf = NULL; + self->size = 0; + self->cap = 0; + S_grow(self, amount); + return self; +} + +ByteBuf* +BB_new_bytes(const void *bytes, size_t size) { + ByteBuf *self = (ByteBuf*)VTable_Make_Obj(BYTEBUF); + BB_init(self, size); + memcpy(self->buf, bytes, size); + self->size = size; + return self; +} + +ByteBuf* +BB_new_steal_bytes(void *bytes, size_t size, size_t capacity) { + ByteBuf *self = (ByteBuf*)VTable_Make_Obj(BYTEBUF); + self->buf = (char*)bytes; + self->size = size; + self->cap = capacity; + return self; +} + +void +BB_destroy(ByteBuf *self) { + FREEMEM(self->buf); + SUPER_DESTROY(self, BYTEBUF); +} + +ByteBuf* +BB_clone(ByteBuf *self) { + return BB_new_bytes(self->buf, self->size); +} + +void +BB_set_size(ByteBuf *self, size_t size) { + if (size > self->cap) { + THROW(ERR, "Can't set size to %u64 (greater than capacity of %u64)", + (uint64_t)size, (uint64_t)self->cap); + } + self->size = size; +} + +char* +BB_get_buf(ByteBuf *self) { + return self->buf; +} + +size_t +BB_get_size(ByteBuf *self) { + return self->size; +} + +size_t +BB_get_capacity(ByteBuf *self) { + return self->cap; +} + +static INLINE bool_t +SI_equals_bytes(ByteBuf *self, const void *bytes, size_t size) { + if (self->size != size) { return false; } + return (memcmp(self->buf, bytes, self->size) == 0); +} + +bool_t +BB_equals(ByteBuf *self, Obj *other) { + ByteBuf *const twin = (ByteBuf*)other; + if (twin == self) { return true; } + if (!Obj_Is_A(other, BYTEBUF)) { return false; } + return SI_equals_bytes(self, twin->buf, twin->size); +} + +bool_t +BB_equals_bytes(ByteBuf *self, const void *bytes, size_t size) { + return SI_equals_bytes(self, bytes, size); +} + +int32_t +BB_hash_sum(ByteBuf *self) { + uint32_t sum = 5381; + uint8_t *const buf = (uint8_t*)self->buf; + + for (size_t i = 0, max = self->size; i < max; i++) { + sum = ((sum << 5) + sum) ^ buf[i]; + } + + return (int32_t)sum; +} + +static INLINE void +SI_mimic_bytes(ByteBuf *self, const void *bytes, size_t size) { + if (size > self->cap) { S_grow(self, size); } + memmove(self->buf, bytes, size); + self->size = size; +} + +void +BB_mimic_bytes(ByteBuf *self, const void *bytes, size_t size) { + SI_mimic_bytes(self, bytes, size); +} + +void +BB_mimic(ByteBuf *self, Obj *other) { + ByteBuf *twin = (ByteBuf*)CERTIFY(other, BYTEBUF); + SI_mimic_bytes(self, twin->buf, twin->size); +} + +static INLINE void +SI_cat_bytes(ByteBuf *self, const void *bytes, size_t size) { + const size_t new_size = self->size + size; + if (new_size > self->cap) { + S_grow(self, Memory_oversize(new_size, sizeof(char))); + } + memcpy((self->buf + self->size), bytes, size); + self->size = new_size; +} + +void +BB_cat_bytes(ByteBuf *self, const void *bytes, size_t size) { + SI_cat_bytes(self, bytes, size); +} + +void +BB_cat(ByteBuf *self, const ByteBuf *other) { + SI_cat_bytes(self, other->buf, other->size); +} + +static void +S_grow(ByteBuf *self, size_t size) { + if (size > self->cap) { + size_t amount = size; + size_t remainder = amount % sizeof(int64_t); + if (remainder) { + amount += sizeof(int64_t); + amount -= remainder; + } + self->buf = (char*)REALLOCATE(self->buf, amount); + self->cap = amount; + } +} + +char* +BB_grow(ByteBuf *self, size_t size) { + if (size > self->cap) { S_grow(self, size); } + return self->buf; +} + +void +BB_serialize(ByteBuf *self, OutStream *target) { + OutStream_Write_C32(target, self->size); + OutStream_Write_Bytes(target, self->buf, self->size); +} + +ByteBuf* +BB_deserialize(ByteBuf *self, InStream *instream) { + const size_t size = InStream_Read_C32(instream); + const size_t capacity = size ? size : sizeof(int64_t); + if (capacity > self->cap) { S_grow(self, capacity); } + self->size = size; + InStream_Read_Bytes(instream, self->buf, size); + return self; +} + +int +BB_compare(const void *va, const void *vb) { + const ByteBuf *a = *(const ByteBuf**)va; + const ByteBuf *b = *(const ByteBuf**)vb; + const size_t size = a->size < b->size ? a->size : b->size; + + int32_t comparison = memcmp(a->buf, b->buf, size); + + if (comparison == 0 && a->size != b->size) { + comparison = a->size < b->size ? -1 : 1; + } + + return comparison; +} + +int32_t +BB_compare_to(ByteBuf *self, Obj *other) { + CERTIFY(other, BYTEBUF); + return BB_compare(&self, &other); +} + +/******************************************************************/ + +ViewByteBuf* +ViewBB_new(char *buf, size_t size) { + ViewByteBuf *self = (ViewByteBuf*)VTable_Make_Obj(VIEWBYTEBUF); + return ViewBB_init(self, buf, size); +} + +ViewByteBuf* +ViewBB_init(ViewByteBuf *self, char *buf, size_t size) { + self->cap = 0; + self->buf = buf; + self->size = size; + return self; +} + +void +ViewBB_destroy(ViewByteBuf *self) { + Obj_destroy((Obj*)self); +} + +void +ViewBB_assign_bytes(ViewByteBuf *self, char*buf, size_t size) { + self->buf = buf; + self->size = size; +} + +void +ViewBB_assign(ViewByteBuf *self, const ByteBuf *other) { + self->buf = other->buf; + self->size = other->size; +} + + diff --git a/clownfish/core/Clownfish/ByteBuf.cfh b/clownfish/core/Clownfish/ByteBuf.cfh new file mode 100644 index 000000000..1b9b61f00 --- /dev/null +++ b/clownfish/core/Clownfish/ByteBuf.cfh @@ -0,0 +1,159 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** + * Growable buffer holding arbitrary bytes. + */ + +class Clownfish::ByteBuf cnick BB inherits Clownfish::Obj { + + char *buf; + size_t size; /* number of valid bytes */ + size_t cap; /* allocated bytes, including terminating null */ + + /** + * @param capacity initial capacity of the ByteBuf, in bytes. + */ + inert incremented ByteBuf* + new(size_t capacity); + + inert ByteBuf* + init(ByteBuf *self, size_t capacity); + + /** Return a pointer to a new ByteBuf which holds a copy of the passed-in + * string. + */ + inert incremented ByteBuf* + new_bytes(const void *bytes, size_t size); + + /** Return a pointer to a new ByteBuf which assumes ownership of the + * passed-in string. + */ + inert incremented ByteBuf* + new_steal_bytes(void *bytes, size_t size, size_t capacity); + + /** Lexical comparison of two ByteBufs, with level of indirection set to + * please qsort and friends. + */ + inert int + compare(const void *va, const void *vb); + + /** Set the object's size member. If greater than the object's capacity, + * throws an error. + */ + void + Set_Size(ByteBuf *self, size_t size); + + /** Accessor for "size" member. + */ + size_t + Get_Size(ByteBuf *self); + + /** Accessor for raw internal buffer. + */ + nullable char* + Get_Buf(ByteBuf *self); + + /** Return the number of bytes in the Object's allocation. + */ + size_t + Get_Capacity(ByteBuf *self); + + public void + Mimic(ByteBuf *self, Obj *other); + + void + Mimic_Bytes(ByteBuf *self, const void *bytes, size_t size); + + /** Concatenate the passed-in bytes onto the end of the ByteBuf. Allocate + * more memory as needed. + */ + void + Cat_Bytes(ByteBuf *self, const void *bytes, size_t size); + + /** Concatenate the contents of other onto the end of the + * original ByteBuf. Allocate more memory as needed. + */ + void + Cat(ByteBuf *self, const ByteBuf *other); + + /** Assign more memory to the ByteBuf, if it doesn't already have enough + * room to hold size bytes. Cannot shrink the allocation. + * + * @return a pointer to the raw buffer. + */ + nullable char* + Grow(ByteBuf *self, size_t size); + + /** Test whether the ByteBuf matches the passed-in bytes. + */ + bool_t + Equals_Bytes(ByteBuf *self, const void *bytes, size_t size); + + public int32_t + Compare_To(ByteBuf *self, Obj *other); + + public incremented ByteBuf* + Clone(ByteBuf *self); + + public void + Destroy(ByteBuf *self); + + public bool_t + Equals(ByteBuf *self, Obj *other); + + public int32_t + Hash_Sum(ByteBuf *self); + + public void + Serialize(ByteBuf *self, OutStream *outstream); + + public incremented ByteBuf* + Deserialize(decremented ByteBuf *self, InStream *instream); +} + +/** + * A ByteBuf that doesn't own its own string. + */ +class Clownfish::ViewByteBuf cnick ViewBB + inherits Clownfish::ByteBuf { + + /** Return a pointer to a new "view" ByteBuf, offing a persective on the + * passed-in string. + */ + inert incremented ViewByteBuf* + new(char *buf, size_t size); + + inert incremented ViewByteBuf* + init(ViewByteBuf *self, char *buf, size_t size); + + /** Assign buf and size members to the passed in values. + */ + void + Assign_Bytes(ViewByteBuf *self, char *buf, size_t size); + + /** Assign buf and size members from the passed-in ByteBuf. + */ + void + Assign(ViewByteBuf *self, const ByteBuf *other); + + public void + Destroy(ViewByteBuf *self); +} + + diff --git a/clownfish/core/Clownfish/CharBuf.c b/clownfish/core/Clownfish/CharBuf.c new file mode 100644 index 000000000..553ce863f --- /dev/null +++ b/clownfish/core/Clownfish/CharBuf.c @@ -0,0 +1,979 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_CHARBUF +#define C_LUCY_VIEWCHARBUF +#define C_LUCY_ZOMBIECHARBUF +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include +#include +#include +#include + +#include "Clownfish/VTable.h" +#include "Clownfish/CharBuf.h" + +#include "Clownfish/Err.h" +#include "Lucy/Store/InStream.h" +#include "Lucy/Store/OutStream.h" +#include "Clownfish/Util/Memory.h" +#include "Clownfish/Util/StringHelper.h" + +// Helper function for throwing invalid UTF-8 error. Since THROW uses +// a CharBuf internally, calling THROW with invalid UTF-8 would create an +// infinite loop -- so we fwrite some of the bogus text to stderr and +// invoke THROW with a generic message. +#define DIE_INVALID_UTF8(text, size) \ + S_die_invalid_utf8(text, size, __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO) +static void +S_die_invalid_utf8(const char *text, size_t size, const char *file, int line, + const char *func); + +// Helper function for throwing invalid pattern error. +static void +S_die_invalid_pattern(const char *pattern); + +CharBuf* +CB_new(size_t size) { + CharBuf *self = (CharBuf*)VTable_Make_Obj(CHARBUF); + return CB_init(self, size); +} + +CharBuf* +CB_init(CharBuf *self, size_t size) { + // Derive. + self->ptr = (char*)MALLOCATE(size + 1); + + // Init. + *self->ptr = '\0'; // Empty string. + + // Assign. + self->size = 0; + self->cap = size + 1; + + return self; +} + +CharBuf* +CB_new_from_utf8(const char *ptr, size_t size) { + if (!StrHelp_utf8_valid(ptr, size)) { + DIE_INVALID_UTF8(ptr, size); + } + return CB_new_from_trusted_utf8(ptr, size); +} + +CharBuf* +CB_new_from_trusted_utf8(const char *ptr, size_t size) { + CharBuf *self = (CharBuf*)VTable_Make_Obj(CHARBUF); + + // Derive. + self->ptr = (char*)MALLOCATE(size + 1); + + // Copy. + memcpy(self->ptr, ptr, size); + + // Assign. + self->size = size; + self->cap = size + 1; + self->ptr[size] = '\0'; // Null terminate. + + return self; +} + +CharBuf* +CB_new_steal_from_trusted_str(char *ptr, size_t size, size_t cap) { + CharBuf *self = (CharBuf*)VTable_Make_Obj(CHARBUF); + self->ptr = ptr; + self->size = size; + self->cap = cap; + return self; +} + +CharBuf* +CB_new_steal_str(char *ptr, size_t size, size_t cap) { + if (!StrHelp_utf8_valid(ptr, size)) { + DIE_INVALID_UTF8(ptr, size); + } + return CB_new_steal_from_trusted_str(ptr, size, cap); +} + +CharBuf* +CB_newf(const char *pattern, ...) { + CharBuf *self = CB_new(strlen(pattern)); + va_list args; + va_start(args, pattern); + CB_VCatF(self, pattern, args); + va_end(args); + return self; +} + +void +CB_destroy(CharBuf *self) { + FREEMEM(self->ptr); + SUPER_DESTROY(self, CHARBUF); +} + +int32_t +CB_hash_sum(CharBuf *self) { + uint32_t hashvalue = 5381; + ZombieCharBuf *iterator = ZCB_WRAP(self); + + const CB_Nip_One_t nip_one = METHOD_PTR(iterator->vtable, Lucy_CB_Nip_One); + while (iterator->size) { + uint32_t code_point = (uint32_t)nip_one((CharBuf*)iterator); + hashvalue = ((hashvalue << 5) + hashvalue) ^ code_point; + } + + return (int32_t) hashvalue; +} + +static void +S_grow(CharBuf *self, size_t size) { + if (size >= self->cap) { + CB_Grow(self, size); + } +} + +char* +CB_grow(CharBuf *self, size_t size) { + if (size >= self->cap) { + self->cap = size + 1; + self->ptr = (char*)REALLOCATE(self->ptr, self->cap); + } + return self->ptr; +} + +static void +S_die_invalid_utf8(const char *text, size_t size, const char *file, int line, + const char *func) { + fprintf(stderr, "Invalid UTF-8, aborting: '"); + fwrite(text, sizeof(char), size < 200 ? size : 200, stderr); + if (size > 200) { fwrite("[...]", sizeof(char), 5, stderr); } + fprintf(stderr, "' (length %lu)\n", (unsigned long)size); + Err_throw_at(ERR, file, line, func, "Invalid UTF-8"); +} + +static void +S_die_invalid_pattern(const char *pattern) { + size_t pattern_len = strlen(pattern); + fprintf(stderr, "Invalid pattern, aborting: '"); + fwrite(pattern, sizeof(char), pattern_len, stderr); + fprintf(stderr, "'\n"); + THROW(ERR, "Invalid pattern."); +} + +void +CB_setf(CharBuf *self, const char *pattern, ...) { + va_list args; + CB_Set_Size(self, 0); + va_start(args, pattern); + CB_VCatF(self, pattern, args); + va_end(args); +} + +void +CB_catf(CharBuf *self, const char *pattern, ...) { + va_list args; + va_start(args, pattern); + CB_VCatF(self, pattern, args); + va_end(args); +} + +void +CB_vcatf(CharBuf *self, const char *pattern, va_list args) { + size_t pattern_len = strlen(pattern); + const char *pattern_start = pattern; + const char *pattern_end = pattern + pattern_len; + char buf[64]; + + for (; pattern < pattern_end; pattern++) { + const char *slice_end = pattern; + + // Consume all characters leading up to a '%'. + while (slice_end < pattern_end && *slice_end != '%') { slice_end++; } + if (pattern != slice_end) { + size_t size = slice_end - pattern; + CB_Cat_Trusted_Str(self, pattern, size); + pattern = slice_end; + } + + if (pattern < pattern_end) { + pattern++; // Move past '%'. + + switch (*pattern) { + case '%': { + CB_Cat_Trusted_Str(self, "%", 1); + } + break; + case 'o': { + Obj *obj = va_arg(args, Obj*); + if (!obj) { + CB_Cat_Trusted_Str(self, "[NULL]", 6); + } + else if (Obj_Is_A(obj, CHARBUF)) { + CB_Cat(self, (CharBuf*)obj); + } + else { + CharBuf *string = Obj_To_String(obj); + CB_Cat(self, string); + DECREF(string); + } + } + break; + case 'i': { + int64_t val = 0; + size_t size; + if (pattern[1] == '8') { + val = va_arg(args, int32_t); + pattern++; + } + else if (pattern[1] == '3' && pattern[2] == '2') { + val = va_arg(args, int32_t); + pattern += 2; + } + else if (pattern[1] == '6' && pattern[2] == '4') { + val = va_arg(args, int64_t); + pattern += 2; + } + else { + S_die_invalid_pattern(pattern_start); + } + size = sprintf(buf, "%" I64P, val); + CB_Cat_Trusted_Str(self, buf, size); + } + break; + case 'u': { + uint64_t val = 0; + size_t size; + if (pattern[1] == '8') { + val = va_arg(args, uint32_t); + pattern += 1; + } + else if (pattern[1] == '3' && pattern[2] == '2') { + val = va_arg(args, uint32_t); + pattern += 2; + } + else if (pattern[1] == '6' && pattern[2] == '4') { + val = va_arg(args, uint64_t); + pattern += 2; + } + else { + S_die_invalid_pattern(pattern_start); + } + size = sprintf(buf, "%" U64P, val); + CB_Cat_Trusted_Str(self, buf, size); + } + break; + case 'f': { + if (pattern[1] == '6' && pattern[2] == '4') { + double num = va_arg(args, double); + char bigbuf[512]; + size_t size = sprintf(bigbuf, "%g", num); + CB_Cat_Trusted_Str(self, bigbuf, size); + pattern += 2; + } + else { + S_die_invalid_pattern(pattern_start); + } + } + break; + case 'x': { + if (pattern[1] == '3' && pattern[2] == '2') { + unsigned long val = va_arg(args, uint32_t); + size_t size = sprintf(buf, "%.8lx", val); + CB_Cat_Trusted_Str(self, buf, size); + pattern += 2; + } + else { + S_die_invalid_pattern(pattern_start); + } + } + break; + case 's': { + char *string = va_arg(args, char*); + if (string == NULL) { + CB_Cat_Trusted_Str(self, "[NULL]", 6); + } + else { + size_t size = strlen(string); + if (StrHelp_utf8_valid(string, size)) { + CB_Cat_Trusted_Str(self, string, size); + } + else { + CB_Cat_Trusted_Str(self, "[INVALID UTF8]", 14); + } + } + } + break; + default: { + // Assume NULL-terminated pattern string, which + // eliminates the need for bounds checking if '%' is + // the last visible character. + S_die_invalid_pattern(pattern_start); + } + } + } + } +} + +CharBuf* +CB_to_string(CharBuf *self) { + return CB_new_from_trusted_utf8(self->ptr, self->size); +} + +void +CB_cat_char(CharBuf *self, uint32_t code_point) { + const size_t MAX_UTF8_BYTES = 4; + if (self->size + MAX_UTF8_BYTES >= self->cap) { + S_grow(self, Memory_oversize(self->size + MAX_UTF8_BYTES, + sizeof(char))); + } + char *end = self->ptr + self->size; + size_t count = StrHelp_encode_utf8_char(code_point, (uint8_t*)end); + self->size += count; + *(end + count) = '\0'; +} + +int32_t +CB_swap_chars(CharBuf *self, uint32_t match, uint32_t replacement) { + int32_t num_swapped = 0; + + if (match > 127) { + THROW(ERR, "match point too high: %u32", match); + } + else if (replacement > 127) { + THROW(ERR, "replacement code point too high: %u32", replacement); + } + else { + char *ptr = self->ptr; + char *const limit = ptr + self->size; + for (; ptr < limit; ptr++) { + if (*ptr == (char)match) { + *ptr = (char)replacement; + num_swapped++; + } + } + } + + return num_swapped; +} + +int64_t +CB_to_i64(CharBuf *self) { + return CB_BaseX_To_I64(self, 10); +} + +int64_t +CB_basex_to_i64(CharBuf *self, uint32_t base) { + ZombieCharBuf *iterator = ZCB_WRAP(self); + int64_t retval = 0; + bool_t is_negative = false; + + // Advance past minus sign. + if (ZCB_Code_Point_At(iterator, 0) == '-') { + ZCB_Nip_One(iterator); + is_negative = true; + } + + // Accumulate. + while (iterator->size) { + int32_t code_point = ZCB_Nip_One(iterator); + if (isalnum(code_point)) { + int32_t addend = isdigit(code_point) + ? code_point - '0' + : tolower(code_point) - 'a' + 10; + if (addend > (int32_t)base) { break; } + retval *= base; + retval += addend; + } + else { + break; + } + } + + // Apply minus sign. + if (is_negative) { retval = 0 - retval; } + + return retval; +} + +static double +S_safe_to_f64(CharBuf *self) { + size_t amount = self->size < 511 ? self->size : 511; + char buf[512]; + memcpy(buf, self->ptr, amount); + buf[amount] = 0; // NULL-terminate. + return strtod(buf, NULL); +} + +double +CB_to_f64(CharBuf *self) { + char *end; + double value = strtod(self->ptr, &end); + size_t consumed = end - self->ptr; + if (consumed > self->size) { // strtod overran + value = S_safe_to_f64(self); + } + return value; +} + +CharBuf* +CB_to_cb8(CharBuf *self) { + return CB_new_from_trusted_utf8(self->ptr, self->size); +} + +CharBuf* +CB_clone(CharBuf *self) { + return CB_new_from_trusted_utf8(self->ptr, self->size); +} + +CharBuf* +CB_load(CharBuf *self, Obj *dump) { + CharBuf *source = (CharBuf*)CERTIFY(dump, CHARBUF); + UNUSED_VAR(self); + return CB_Clone(source); +} + +void +CB_serialize(CharBuf *self, OutStream *target) { + OutStream_Write_C32(target, self->size); + OutStream_Write_Bytes(target, self->ptr, self->size); +} + +CharBuf* +CB_deserialize(CharBuf *self, InStream *instream) { + size_t size = InStream_Read_C32(instream); + if (size >= self->cap) { S_grow(self, size); } + InStream_Read_Bytes(instream, self->ptr, size); + self->size = size; + self->ptr[size] = '\0'; + if (!StrHelp_utf8_valid(self->ptr, size)) { + DIE_INVALID_UTF8(self->ptr, size); + } + return self; +} + +void +CB_mimic_str(CharBuf *self, const char* ptr, size_t size) { + if (!StrHelp_utf8_valid(ptr, size)) { + DIE_INVALID_UTF8(ptr, size); + } + if (size >= self->cap) { S_grow(self, size); } + memmove(self->ptr, ptr, size); + self->size = size; + self->ptr[size] = '\0'; +} + +void +CB_mimic(CharBuf *self, Obj *other) { + CharBuf *twin = (CharBuf*)CERTIFY(other, CHARBUF); + if (twin->size >= self->cap) { S_grow(self, twin->size); } + memmove(self->ptr, twin->ptr, twin->size); + self->size = twin->size; + self->ptr[twin->size] = '\0'; +} + +void +CB_cat_str(CharBuf *self, const char* ptr, size_t size) { + if (!StrHelp_utf8_valid(ptr, size)) { + DIE_INVALID_UTF8(ptr, size); + } + CB_cat_trusted_str(self, ptr, size); +} + +void +CB_cat_trusted_str(CharBuf *self, const char* ptr, size_t size) { + const size_t new_size = self->size + size; + if (new_size >= self->cap) { + size_t amount = Memory_oversize(new_size, sizeof(char)); + S_grow(self, amount); + } + memcpy((self->ptr + self->size), ptr, size); + self->size = new_size; + self->ptr[new_size] = '\0'; +} + +void +CB_cat(CharBuf *self, const CharBuf *other) { + const size_t new_size = self->size + other->size; + if (new_size >= self->cap) { + size_t amount = Memory_oversize(new_size, sizeof(char)); + S_grow(self, amount); + } + memcpy((self->ptr + self->size), other->ptr, other->size); + self->size = new_size; + self->ptr[new_size] = '\0'; +} + +bool_t +CB_starts_with(CharBuf *self, const CharBuf *prefix) { + return CB_starts_with_str(self, prefix->ptr, prefix->size); +} + +bool_t +CB_starts_with_str(CharBuf *self, const char *prefix, size_t size) { + if (size <= self->size + && (memcmp(self->ptr, prefix, size) == 0) + ) { + return true; + } + else { + return false; + } +} + +bool_t +CB_equals(CharBuf *self, Obj *other) { + CharBuf *const twin = (CharBuf*)other; + if (twin == self) { return true; } + if (!Obj_Is_A(other, CHARBUF)) { return false; } + return CB_equals_str(self, twin->ptr, twin->size); +} + +int32_t +CB_compare_to(CharBuf *self, Obj *other) { + CERTIFY(other, CHARBUF); + return CB_compare(&self, &other); +} + +bool_t +CB_equals_str(CharBuf *self, const char *ptr, size_t size) { + if (self->size != size) { + return false; + } + return (memcmp(self->ptr, ptr, self->size) == 0); +} + +bool_t +CB_ends_with(CharBuf *self, const CharBuf *postfix) { + return CB_ends_with_str(self, postfix->ptr, postfix->size); +} + +bool_t +CB_ends_with_str(CharBuf *self, const char *postfix, size_t postfix_len) { + if (postfix_len <= self->size) { + char *start = self->ptr + self->size - postfix_len; + if (memcmp(start, postfix, postfix_len) == 0) { + return true; + } + } + + return false; +} + +int64_t +CB_find(CharBuf *self, const CharBuf *substring) { + return CB_Find_Str(self, substring->ptr, substring->size); +} + +int64_t +CB_find_str(CharBuf *self, const char *ptr, size_t size) { + ZombieCharBuf *iterator = ZCB_WRAP(self); + int64_t location = 0; + + while (iterator->size) { + if (ZCB_Starts_With_Str(iterator, ptr, size)) { + return location; + } + ZCB_Nip(iterator, 1); + location++; + } + + return -1; +} + +uint32_t +CB_trim(CharBuf *self) { + return CB_Trim_Top(self) + CB_Trim_Tail(self); +} + +uint32_t +CB_trim_top(CharBuf *self) { + char *ptr = self->ptr; + char *end = ptr + self->size; + uint32_t count = 0; + + while (ptr < end) { + uint32_t code_point = StrHelp_decode_utf8_char(ptr); + if (!StrHelp_is_whitespace(code_point)) { break; } + ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr]; + count++; + } + if (ptr > end) { + DIE_INVALID_UTF8(self->ptr, self->size); + } + + if (count) { + // Copy string backwards. + self->size = end - ptr; + memmove(self->ptr, ptr, self->size); + } + + return count; +} + +uint32_t +CB_trim_tail(CharBuf *self) { + uint32_t count = 0; + char *const top = self->ptr; + const char *ptr = top + self->size; + size_t new_size = self->size; + + while (NULL != (ptr = StrHelp_back_utf8_char(ptr, top))) { + uint32_t code_point = StrHelp_decode_utf8_char(ptr); + if (!StrHelp_is_whitespace(code_point)) { break; } + new_size = ptr - top; + count++; + } + self->size = new_size; + + return count; +} + +size_t +CB_nip(CharBuf *self, size_t count) { + size_t num_nipped = 0; + char *ptr = self->ptr; + char *const end = ptr + self->size; + for (; ptr < end && count--; ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr]) { + num_nipped++; + } + if (ptr > end) { + DIE_INVALID_UTF8(self->ptr, self->size); + } + self->size = end - ptr; + memmove(self->ptr, ptr, self->size); + return num_nipped; +} + +int32_t +CB_nip_one(CharBuf *self) { + if (self->size == 0) { + return 0; + } + else { + int32_t retval = (int32_t)StrHelp_decode_utf8_char(self->ptr); + size_t consumed = StrHelp_UTF8_COUNT[*(uint8_t*)self->ptr]; + if (consumed > self->size) { + DIE_INVALID_UTF8(self->ptr, self->size); + } + char *ptr = self->ptr + StrHelp_UTF8_COUNT[*(uint8_t*)self->ptr]; + self->size -= consumed; + memmove(self->ptr, ptr, self->size); + return retval; + } +} + +size_t +CB_chop(CharBuf *self, size_t count) { + size_t num_chopped = 0; + char *top = self->ptr; + const char *ptr = top + self->size; + for (num_chopped = 0; num_chopped < count; num_chopped++) { + const char *end = ptr; + if (NULL == (ptr = StrHelp_back_utf8_char(ptr, top))) { break; } + self->size -= (end - ptr); + } + return num_chopped; +} + +size_t +CB_length(CharBuf *self) { + size_t len = 0; + char *ptr = self->ptr; + char *end = ptr + self->size; + while (ptr < end) { + ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr]; + len++; + } + if (ptr != end) { + DIE_INVALID_UTF8(self->ptr, self->size); + } + return len; +} + +size_t +CB_truncate(CharBuf *self, size_t count) { + uint32_t num_code_points; + ZombieCharBuf *iterator = ZCB_WRAP(self); + num_code_points = ZCB_Nip(iterator, count); + self->size -= iterator->size; + return num_code_points; +} + +uint32_t +CB_code_point_at(CharBuf *self, size_t tick) { + size_t count = 0; + char *ptr = self->ptr; + char *const end = ptr + self->size; + + for (; ptr < end; ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr]) { + if (count == tick) { + if (ptr > end) { + DIE_INVALID_UTF8(self->ptr, self->size); + } + return StrHelp_decode_utf8_char(ptr); + } + count++; + } + + return 0; +} + +uint32_t +CB_code_point_from(CharBuf *self, size_t tick) { + size_t count = 0; + char *top = self->ptr; + const char *ptr = top + self->size; + + for (count = 0; count < tick; count++) { + if (NULL == (ptr = StrHelp_back_utf8_char(ptr, top))) { return 0; } + } + return StrHelp_decode_utf8_char(ptr); +} + +CharBuf* +CB_substring(CharBuf *self, size_t offset, size_t len) { + ZombieCharBuf *iterator = ZCB_WRAP(self); + char *sub_start; + size_t byte_len; + + ZCB_Nip(iterator, offset); + sub_start = iterator->ptr; + ZCB_Nip(iterator, len); + byte_len = iterator->ptr - sub_start; + + return CB_new_from_trusted_utf8(sub_start, byte_len); +} + +int +CB_compare(const void *va, const void *vb) { + const CharBuf *a = *(const CharBuf**)va; + const CharBuf *b = *(const CharBuf**)vb; + ZombieCharBuf *iterator_a = ZCB_WRAP(a); + ZombieCharBuf *iterator_b = ZCB_WRAP(b); + while (iterator_a->size && iterator_b->size) { + int32_t code_point_a = ZCB_Nip_One(iterator_a); + int32_t code_point_b = ZCB_Nip_One(iterator_b); + const int32_t comparison = code_point_a - code_point_b; + if (comparison != 0) { return comparison; } + } + if (iterator_a->size != iterator_b->size) { + return iterator_a->size < iterator_b->size ? -1 : 1; + } + return 0; +} + +bool_t +CB_less_than(const void *va, const void *vb) { + return CB_compare(va, vb) < 0 ? 1 : 0; +} + +void +CB_set_size(CharBuf *self, size_t size) { + self->size = size; +} + +size_t +CB_get_size(CharBuf *self) { + return self->size; +} + +uint8_t* +CB_get_ptr8(CharBuf *self) { + return (uint8_t*)self->ptr; +} + +/*****************************************************************/ + +ViewCharBuf* +ViewCB_new_from_utf8(const char *utf8, size_t size) { + if (!StrHelp_utf8_valid(utf8, size)) { + DIE_INVALID_UTF8(utf8, size); + } + return ViewCB_new_from_trusted_utf8(utf8, size); +} + +ViewCharBuf* +ViewCB_new_from_trusted_utf8(const char *utf8, size_t size) { + ViewCharBuf *self = (ViewCharBuf*)VTable_Make_Obj(VIEWCHARBUF); + return ViewCB_init(self, utf8, size); +} + +ViewCharBuf* +ViewCB_init(ViewCharBuf *self, const char *utf8, size_t size) { + self->ptr = (char*)utf8; + self->size = size; + self->cap = 0; + return self; +} + +void +ViewCB_destroy(ViewCharBuf *self) { + // Note that we do not free self->ptr, and that we invoke the + // SUPER_DESTROY with CHARBUF instead of VIEWCHARBUF. + SUPER_DESTROY(self, CHARBUF); +} + +void +ViewCB_assign(ViewCharBuf *self, const CharBuf *other) { + self->ptr = other->ptr; + self->size = other->size; +} + +void +ViewCB_assign_str(ViewCharBuf *self, const char *utf8, size_t size) { + if (!StrHelp_utf8_valid(utf8, size)) { + DIE_INVALID_UTF8(utf8, size); + } + self->ptr = (char*)utf8; + self->size = size; +} + +void +ViewCB_assign_trusted_str(ViewCharBuf *self, const char *utf8, size_t size) { + self->ptr = (char*)utf8; + self->size = size; +} + +uint32_t +ViewCB_trim_top(ViewCharBuf *self) { + uint32_t count = 0; + char *ptr = self->ptr; + char *end = ptr + self->size; + + while (ptr < end) { + uint32_t code_point = StrHelp_decode_utf8_char(ptr); + if (!StrHelp_is_whitespace(code_point)) { break; } + ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr]; + count++; + } + + if (count) { + if (ptr > end) { + DIE_INVALID_UTF8(self->ptr, self->size); + } + self->size = end - ptr; + self->ptr = ptr; + } + + return count; +} + +size_t +ViewCB_nip(ViewCharBuf *self, size_t count) { + size_t num_nipped; + char *ptr = self->ptr; + char *end = ptr + self->size; + for (num_nipped = 0; + ptr < end && count--; + ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr] + ) { + num_nipped++; + } + if (ptr > end) { + DIE_INVALID_UTF8(self->ptr, self->size); + } + self->size = end - ptr; + self->ptr = ptr; + return num_nipped; +} + +int32_t +ViewCB_nip_one(ViewCharBuf *self) { + if (self->size == 0) { + return 0; + } + else { + int32_t retval = (int32_t)StrHelp_decode_utf8_char(self->ptr); + size_t consumed = StrHelp_UTF8_COUNT[*(uint8_t*)self->ptr]; + if (consumed > self->size) { + DIE_INVALID_UTF8(self->ptr, self->size); + } + self->ptr += consumed; + self->size -= consumed; + return retval; + } +} + +char* +ViewCB_grow(ViewCharBuf *self, size_t size) { + UNUSED_VAR(size); + THROW(ERR, "Can't grow a ViewCharBuf ('%o')", self); + UNREACHABLE_RETURN(char*); +} + +/*****************************************************************/ + +ZombieCharBuf* +ZCB_new(void *allocation) { + static char empty_string[] = ""; + ZombieCharBuf *self = (ZombieCharBuf*)allocation; + self->ref.count = 1; + self->vtable = ZOMBIECHARBUF; + self->cap = 0; + self->size = 0; + self->ptr = empty_string; + return self; +} + +ZombieCharBuf* +ZCB_newf(void *allocation, size_t alloc_size, const char *pattern, ...) { + ZombieCharBuf *self = (ZombieCharBuf*)allocation; + + self->ref.count = 1; + self->vtable = ZOMBIECHARBUF; + self->cap = alloc_size - sizeof(ZombieCharBuf); + self->size = 0; + self->ptr = ((char*)allocation) + sizeof(ZombieCharBuf); + + va_list args; + va_start(args, pattern); + ZCB_VCatF(self, pattern, args); + va_end(args); + + return self; +} + +ZombieCharBuf* +ZCB_wrap_str(void *allocation, const char *ptr, size_t size) { + ZombieCharBuf *self = (ZombieCharBuf*)allocation; + self->ref.count = 1; + self->vtable = ZOMBIECHARBUF; + self->cap = 0; + self->size = size; + self->ptr = (char*)ptr; + return self; +} + +ZombieCharBuf* +ZCB_wrap(void *allocation, const CharBuf *source) { + return ZCB_wrap_str(allocation, source->ptr, source->size); +} + +size_t +ZCB_size() { + return sizeof(ZombieCharBuf); +} + +void +ZCB_destroy(ZombieCharBuf *self) { + THROW(ERR, "Can't destroy a ZombieCharBuf ('%o')", self); +} + + diff --git a/clownfish/core/Clownfish/CharBuf.cfh b/clownfish/core/Clownfish/CharBuf.cfh new file mode 100644 index 000000000..5acc0e68d --- /dev/null +++ b/clownfish/core/Clownfish/CharBuf.cfh @@ -0,0 +1,405 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** + * Growable buffer holding Unicode characters. + */ + +class Clownfish::CharBuf cnick CB + inherits Clownfish::Obj { + + char *ptr; + size_t size; + size_t cap; /* allocated bytes, including terminating null */ + + inert incremented CharBuf* + new(size_t size); + + inert CharBuf* + init(CharBuf *self, size_t size); + + /** Return a new CharBuf which holds a copy of the passed-in string. + * Check for UTF-8 validity. + */ + inert incremented CharBuf* + new_from_utf8(const char *utf8, size_t size); + + /** Return a new CharBuf which holds a copy of the passed-in string. No + * validity checking is performed. + */ + inert incremented CharBuf* + new_from_trusted_utf8(const char *utf8, size_t size); + + /** Return a pointer to a new CharBuf which assumes ownership of the + * passed-in string. Check validity of supplied UTF-8. + */ + inert incremented CharBuf* + new_steal_str(char *ptr, size_t size, size_t cap); + + /** Return a pointer to a new CharBuf which assumes ownership of the + * passed-in string. Do not check validity of supplied UTF-8. + */ + inert incremented CharBuf* + new_steal_from_trusted_str(char *ptr, size_t size, size_t cap); + + /** Return a pointer to a new CharBuf which contains formatted data + * expanded according to CB_VCatF. + * + * Note: a user-supplied pattern string is a security hole + * and must not be allowed. + */ + inert incremented CharBuf* + newf(const char *pattern, ...); + + /** Perform lexical comparison of two CharBufs, with level of indirection + * set to please qsort and friends. + */ + inert int + compare(const void *va, const void *vb); + + /** Perform lexical comparison of two CharBufs, with level of indirection + * set to please qsort and friends, and return true if a is + * less than b. + */ + inert bool_t + less_than(const void *va, const void *vb); + + public void + Mimic(CharBuf *self, Obj *other); + + void + Mimic_Str(CharBuf *self, const char *ptr, size_t size); + + /** Concatenate the passed-in string onto the end of the CharBuf. + */ + void + Cat_Str(CharBuf *self, const char *ptr, size_t size); + + /** Concatenate the contents of other onto the end of the + * caller. + */ + void + Cat(CharBuf *self, const CharBuf *other); + + /** Concatenate formatted arguments. Similar to the printf family, but + * only accepts minimal options (just enough for decent error messages). + * + * Objects: %o + * char*: %s + * integers: %i8 %i32 %i64 %u8 %u32 %u64 + * floats: %f64 + * hex: %x32 + * + * Note that all Clownfish Objects, including CharBufs, are printed via + * %o (which invokes Obj_To_String()). + */ + void + VCatF(CharBuf *self, const char *pattern, va_list args); + + /** Invokes CB_VCatF to concatenate formatted arguments. Note that this + * is only a function and not a method. + */ + inert void + catf(CharBuf *self, const char *pattern, ...); + + /** Replaces the contents of the CharBuf using formatted arguments. + */ + inert void + setf(CharBuf *self, const char *pattern, ...); + + /** Concatenate one Unicode character onto the end of the CharBuf. + */ + void + Cat_Char(CharBuf *self, uint32_t code_point); + + /** Replace all instances of one character for the other. For now, both + * the source and replacement code points must be ASCII. + */ + int32_t + Swap_Chars(CharBuf *self, uint32_t match, uint32_t replacement); + + public int64_t + To_I64(CharBuf *self); + + /** Extract a 64-bit integer from a variable-base stringified version. + */ + int64_t + BaseX_To_I64(CharBuf *self, uint32_t base); + + public double + To_F64(CharBuf *self); + + /** Assign more memory to the CharBuf, if it doesn't already have enough + * room to hold a string of size bytes. Cannot shrink the + * allocation. + * + * @return a pointer to the raw buffer. + */ + char* + Grow(CharBuf *self, size_t size); + + /** Test whether the CharBuf starts with the content of another. + */ + bool_t + Starts_With(CharBuf *self, const CharBuf *prefix); + + /** Test whether the CharBuf starts with the passed-in string. + */ + bool_t + Starts_With_Str(CharBuf *self, const char *prefix, size_t size); + + /** Test whether the CharBuf ends with the content of another. + */ + bool_t + Ends_With(CharBuf *self, const CharBuf *postfix); + + /** Test whether the CharBuf ends with the passed-in string. + */ + bool_t + Ends_With_Str(CharBuf *self, const char *postfix, size_t size); + + /** Return the location of the substring within the CharBuf (measured in + * code points), or -1 if the substring does not match. + */ + int64_t + Find(CharBuf *self, const CharBuf *substring); + + int64_t + Find_Str(CharBuf *self, const char *ptr, size_t size); + + /** Test whether the CharBuf matches the passed-in string. + */ + bool_t + Equals_Str(CharBuf *self, const char *ptr, size_t size); + + /** Return the number of Unicode code points in the object's string. + */ + size_t + Length(CharBuf *self); + + /** Set the CharBuf's size attribute. + */ + void + Set_Size(CharBuf *self, size_t size); + + /** Get the CharBuf's size attribute. + */ + size_t + Get_Size(CharBuf *self); + + /** Return the internal backing array for the CharBuf if its internal + * encoding is UTF-8. If it is not encoded as UTF-8 throw an exception. + */ + uint8_t* + Get_Ptr8(CharBuf *self); + + /** Return a fresh copy of the string data in a CharBuf with an internal + * encoding of UTF-8. + */ + CharBuf* + To_CB8(CharBuf *self); + + public incremented CharBuf* + Clone(CharBuf *self); + + public void + Destroy(CharBuf *self); + + public bool_t + Equals(CharBuf *self, Obj *other); + + public int32_t + Compare_To(CharBuf *self, Obj *other); + + public int32_t + Hash_Sum(CharBuf *self); + + public incremented CharBuf* + To_String(CharBuf *self); + + public incremented CharBuf* + Load(CharBuf *self, Obj *dump); + + public void + Serialize(CharBuf *self, OutStream *outstream); + + public incremented CharBuf* + Deserialize(decremented CharBuf *self, InStream *instream); + + /** Remove Unicode whitespace characters from both top and tail. + */ + uint32_t + Trim(CharBuf *self); + + /** Remove leading Unicode whitespace. + */ + uint32_t + Trim_Top(CharBuf *self); + + /** Remove trailing Unicode whitespace. + */ + uint32_t + Trim_Tail(CharBuf *self); + + /** Remove characters (measured in code points) from the top of the + * CharBuf. Returns the number nipped. + */ + size_t + Nip(CharBuf *self, size_t count); + + /** Remove one character from the top of the CharBuf. Returns the code + * point, or 0 if the string was empty. + */ + int32_t + Nip_One(CharBuf *self); + + /** Remove characters (measured in code points) from the end of the + * CharBuf. Returns the number chopped. + */ + size_t + Chop(CharBuf *self, size_t count); + + /** Truncate the CharBuf so that it contains no more than + * countcharacters. + * + * @param count Maximum new length, in Unicode code points. + * @return The number of code points left in the string after truncation. + */ + size_t + Truncate(CharBuf *self, size_t count); + + /** Return the Unicode code point at the specified number of code points + * in. Return 0 if the string length is exceeded. (XXX It would be + * better to throw an exception, but that's not practical with UTF-8 and + * no cached length.) + */ + uint32_t + Code_Point_At(CharBuf *self, size_t tick); + + /** Return the Unicode code point at the specified number of code points + * counted backwards from the end of the string. Return 0 if outside the + * string. + */ + uint32_t + Code_Point_From(CharBuf *self, size_t tick); + + /** Return a newly allocated CharBuf containing a copy of the indicated + * substring. + * @param offset Offset from the top, in code points. + * @param len The desired length of the substring, in code points. + */ + incremented CharBuf* + SubString(CharBuf *self, size_t offset, size_t len); + + /** Concatenate the supplied text onto the end of the CharBuf. Don't + * check for UTF-8 validity. + */ + void + Cat_Trusted_Str(CharBuf *self, const char *ptr, size_t size); +} + +class Clownfish::ViewCharBuf cnick ViewCB + inherits Clownfish::CharBuf { + + inert incremented ViewCharBuf* + new_from_utf8(const char *utf8, size_t size); + + inert incremented ViewCharBuf* + new_from_trusted_utf8(const char *utf8, size_t size); + + inert ViewCharBuf* + init(ViewCharBuf *self, const char *utf8, size_t size); + + void + Assign(ViewCharBuf *self, const CharBuf *other); + + void + Assign_Str(ViewCharBuf *self, const char *utf8, size_t size); + + void + Assign_Trusted_Str(ViewCharBuf *self, const char *utf8, size_t size); + + uint32_t + Trim_Top(ViewCharBuf *self); + + size_t + Nip(ViewCharBuf *self, size_t count); + + int32_t + Nip_One(ViewCharBuf *self); + + /** Throws an error. */ + char* + Grow(ViewCharBuf *self, size_t size); + + public void + Destroy(ViewCharBuf *self); +} + +class Clownfish::ZombieCharBuf cnick ZCB + inherits Clownfish::ViewCharBuf { + + /** Return a ZombieCharBuf with a blank string. + */ + inert incremented ZombieCharBuf* + new(void *allocation); + + /** + * @param allocation A single block of memory which will be used for both + * the ZombieCharBuf object and its buffer. + * @param alloc_size The size of the allocation. + * @param pattern A format pattern. + */ + inert incremented ZombieCharBuf* + newf(void *allocation, size_t alloc_size, const char *pattern, ...); + + inert incremented ZombieCharBuf* + wrap(void *allocation, const CharBuf *source); + + inert incremented ZombieCharBuf* + wrap_str(void *allocation, const char *ptr, size_t size); + + /** Return the size for a ZombieCharBuf struct. + */ + inert size_t + size(); + + /** Throws an error. + */ + public void + Destroy(ZombieCharBuf *self); +} + +__C__ + +#define CFISH_ZCB_BLANK() lucy_ZCB_new(alloca(lucy_ZCB_size())) + +#define CFISH_ZCB_WRAP(source) \ + lucy_ZCB_wrap(alloca(lucy_ZCB_size()), source) + +#define CFISH_ZCB_WRAP_STR(ptr, size) \ + lucy_ZCB_wrap_str(alloca(lucy_ZCB_size()), ptr, size) + +#ifdef LUCY_USE_SHORT_NAMES + #define ZCB_BLANK CFISH_ZCB_BLANK + #define ZCB_WRAP CFISH_ZCB_WRAP + #define ZCB_WRAP_STR CFISH_ZCB_WRAP_STR +#endif +__END_C__ + + diff --git a/clownfish/core/Clownfish/Err.c b/clownfish/core/Clownfish/Err.c new file mode 100644 index 000000000..99602f32f --- /dev/null +++ b/clownfish/core/Clownfish/Err.c @@ -0,0 +1,261 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_ERR +#define C_LUCY_OBJ +#define C_LUCY_VTABLE +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include +#include +#include + +#include "Clownfish/Err.h" +#include "Clownfish/CharBuf.h" +#include "Clownfish/VTable.h" +#include "Clownfish/Util/Memory.h" + +Err* +Err_new(CharBuf *mess) { + Err *self = (Err*)VTable_Make_Obj(ERR); + return Err_init(self, mess); +} + +Err* +Err_init(Err *self, CharBuf *mess) { + self->mess = mess; + return self; +} + +void +Err_destroy(Err *self) { + DECREF(self->mess); + SUPER_DESTROY(self, ERR); +} + +Err* +Err_make(Err *self) { + UNUSED_VAR(self); + return Err_new(CB_new(0)); +} + +CharBuf* +Err_to_string(Err *self) { + return (CharBuf*)INCREF(self->mess); +} + +void +Err_cat_mess(Err *self, const CharBuf *mess) { + CB_Cat(self->mess, mess); +} + +// Fallbacks in case variadic macros aren't available. +#ifndef CHY_HAS_VARIADIC_MACROS +void +THROW(VTable *vtable, char *pattern, ...) { + va_list args; + Err_Make_t make = METHOD_PTR(CERTIFY(vtable, VTABLE), Lucy_Err_Make); + Err *err = (Err*)CERTIFY(make(NULL), ERR); + CharBuf *mess = Err_Get_Mess(err); + + va_start(args, pattern); + CB_VCatF(mess, pattern, args); + va_end(args); + + Err_do_throw(err); +} +void +CFISH_WARN(char *pattern, ...) { + va_list args; + CharBuf *const message = CB_new(strlen(pattern) + 10); + + va_start(args, pattern); + CB_VCatF(message, pattern, args); + va_end(args); + + Err_warn_mess(message); +} +CharBuf* +CFISH_MAKE_MESS(char *pattern, ...) { + va_list args; + CharBuf *const message = CB_new(strlen(pattern) + 10); + + va_start(args, pattern); + CB_VCatF(message, pattern, args); + va_end(args); + + return message; +} +#endif + + +static void +S_vcat_mess(CharBuf *message, const char *file, int line, const char *func, + const char *pattern, va_list args) { + size_t guess_len = strlen(file) + + func ? strlen(func) : 0 + + strlen(pattern) + + 30; + CB_Grow(message, guess_len); + CB_VCatF(message, pattern, args); + if (func != NULL) { + CB_catf(message, "\n\t%s at %s line %i32\n", func, file, (int32_t)line); + } + else { + CB_catf(message, "\n\t%s line %i32\n", file, (int32_t)line); + } +} + +CharBuf* +Err_make_mess(const char *file, int line, const char *func, + const char *pattern, ...) { + va_list args; + size_t guess_len = strlen(pattern) + strlen(file) + 20; + CharBuf *message = CB_new(guess_len); + va_start(args, pattern); + S_vcat_mess(message, file, line, func, pattern, args); + va_end(args); + return message; +} + +void +Err_warn_at(const char *file, int line, const char *func, + const char *pattern, ...) { + va_list args; + CharBuf *message = CB_new(0); + va_start(args, pattern); + S_vcat_mess(message, file, line, func, pattern, args); + va_end(args); + Err_warn_mess(message); +} + +CharBuf* +Err_get_mess(Err *self) { + return self->mess; +} + +void +Err_add_frame(Err *self, const char *file, int line, const char *func) { + if (CB_Ends_With_Str(self->mess, "\n", 1)) { CB_Chop(self->mess, 1); } + + if (func != NULL) { + CB_catf(self->mess, "\n\t%s at %s line %i32\n", func, file, + (int32_t)line); + } + else { + CB_catf(self->mess, "\n\tat %s line %i32\n", file, (int32_t)line); + } +} + +void +Err_rethrow(Err *self, const char *file, int line, const char *func) { + Err_add_frame(self, file, line, func); + Err_do_throw(self); +} + +void +Err_throw_at(VTable *vtable, const char *file, int line, + const char *func, const char *pattern, ...) { + va_list args; + Err_Make_t make = METHOD_PTR(CERTIFY(vtable, VTABLE), Lucy_Err_Make); + Err *err = (Err*)CERTIFY(make(NULL), ERR); + CharBuf *mess = Err_Get_Mess(err); + + va_start(args, pattern); + S_vcat_mess(mess, file, line, func, pattern, args); + va_end(args); + + Err_do_throw(err); +} + +// Inlined, slightly optimized version of Obj_is_a. +static INLINE bool_t +SI_obj_is_a(Obj *obj, VTable *target_vtable) { + VTable *vtable = obj->vtable; + + while (vtable != NULL) { + if (vtable == target_vtable) { + return true; + } + vtable = vtable->parent; + } + + return false; +} + +Obj* +Err_downcast(Obj *obj, VTable *vtable, const char *file, int line, + const char *func) { + if (obj && !SI_obj_is_a(obj, vtable)) { + Err_throw_at(ERR, file, line, func, "Can't downcast from %o to %o", + Obj_Get_Class_Name(obj), VTable_Get_Name(vtable)); + } + return obj; +} + +Obj* +Err_certify(Obj *obj, VTable *vtable, const char *file, int line, + const char *func) { + if (!obj) { + Err_throw_at(ERR, file, line, func, "Object isn't a %o, it's NULL", + VTable_Get_Name(vtable)); + } + else if (!SI_obj_is_a(obj, vtable)) { + Err_throw_at(ERR, file, line, func, "Can't downcast from %o to %o", + Obj_Get_Class_Name(obj), VTable_Get_Name(vtable)); + } + return obj; +} + +#ifdef CHY_HAS_WINDOWS_H + +#include + +char* +Err_win_error() { + size_t buf_size = 256; + char *buf = (char*)MALLOCATE(buf_size); + size_t message_len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, // message source table + GetLastError(), + 0, // language id + buf, + buf_size, + NULL // empty va_list + ); + if (message_len == 0) { + char unknown[] = "Unknown error"; + size_t len = sizeof(unknown); + strncpy(buf, unknown, len); + } + else if (message_len > 1) { + // Kill stupid newline. + buf[message_len - 2] = '\0'; + } + return buf; +} + +#else + +char* +Err_win_error() { + return NULL; // Never called. +} + +#endif // CHY_HAS_WINDOWS_H + + diff --git a/clownfish/core/Clownfish/Err.cfh b/clownfish/core/Clownfish/Err.cfh new file mode 100644 index 000000000..51f5b8b7c --- /dev/null +++ b/clownfish/core/Clownfish/Err.cfh @@ -0,0 +1,258 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +__C__ +typedef void +(*Lucy_Err_Attempt_t)(void *context); + +#define Cfish_Err_Attempt_t Lucy_Err_Attempt_t +#ifdef LUCY_USE_SHORT_NAMES + #define Err_Attempt_t Cfish_Err_Attempt_t +#endif +__END_C__ + +/** + * Exception. + * + * Clownfish::Err is the base class for exceptions in the Clownfish object + * hierarchy. + * + * The Err module also provides access to a per-thread Err shared variable via + * set_error() and get_error(). It may be used to store an Err object + * temporarily, so that calling code may choose how to handle a particular + * error condition. + */ +public class Clownfish::Err inherits Clownfish::Obj { + + CharBuf *mess; + + inert void + init_class(); + + inert incremented Err* + new(decremented CharBuf *mess); + + inert Err* + init(Err *self, decremented CharBuf *mess); + + public void + Destroy(Err *self); + + public incremented CharBuf* + To_String(Err *self); + + void* + To_Host(Err *self); + + /** Concatenate the supplied argument onto the internal "mess". + */ + public void + Cat_Mess(Err *self, const CharBuf *mess); + + public CharBuf* + Get_Mess(Err *self); + + /** Add information about the current stack frame onto mess. + */ + void + Add_Frame(Err *self, const char *file, int line, const char *func); + + public incremented Err* + Make(Err *self); + + /** Set the value of "error", a per-thread Err shared variable. + */ + public inert void + set_error(decremented Err *error); + + /** Retrieve per-thread Err shared variable "error". + */ + public inert nullable Err* + get_error(); + + /** Run routine within the host's exception handling + * environment, catching and returning any errors that occur. + * + * If an unrecognized host exception is trapped, it will be wrapped in an + * Err so that it can be handled by Clownfish code. + * + * @return an Err, or NULL if no exception occurs. + */ + public inert incremented nullable Err* + trap(Cfish_Err_Attempt_t routine, void *context); + + /** Print an error message to stderr with some C contextual information. + * Usually invoked via the WARN(pattern, ...) macro. + */ + inert void + warn_at(const char *file, int line, const char *func, + const char *pattern, ...); + + /** Raise an exception. Usually invoked via the THROW macro. + */ + inert void + throw_at(VTable *vtable, const char *file, int line, const char *func, + const char *pattern, ...); + + /** Throw an existing exception after tacking on additional context data. + */ + inert void + rethrow(Err *error, const char *file, int line, const char *func); + + /** Raise an exception. Clean up the supplied message by decrementing its + * refcount. + * + * @param vtable The vtable for the Err class to throw. + * @param message Error message, to be output verbatim. + */ + inert void + throw_mess(VTable *vtable, decremented CharBuf *message); + + /** Invoke host exception handling. + */ + inert void + do_throw(decremented Err *self); + + /** Invoke host warning mechanism. Clean up the supplied message by + * decrementing its refcount. + * + * @param message Error message, to be output verbatim. + */ + inert void + warn_mess(decremented CharBuf *message); + + /** Create a formatted error message. Ususally invoked via the MAKE_MESS + * macro. + */ + inert CharBuf* + make_mess(const char *file, int line, const char *func, + const char *pattern, ...); + + /** Verify that obj is either NULL or inherits from + * the class represented by vtable. + * + * @return the object. + */ + inert nullable Obj* + downcast(Obj *obj, VTable *vtable, const char *file, int line, + const char *func); + + /** Verify that obj is not NULL and inherits from the class + * represented by vtable. + * + * @return the object. + */ + inert Obj* + certify(Obj *obj, VTable *vtable, const char *file, int line, + const char *func); + + /** Verify that an object belongs to a subclass and not an abstract class. + */ + inert inline void + abstract_class_check(Obj *obj, VTable *vtable); + + /** On Windows, return a newly allocated buffer containing the string + * description for the the last error in the thread. + */ + inert char* + win_error(); +} + +__C__ +#ifdef CHY_HAS_FUNC_MACRO + #define CFISH_ERR_FUNC_MACRO CHY_FUNC_MACRO +#else + #define CFISH_ERR_FUNC_MACRO NULL +#endif + +#define CFISH_ERR_ADD_FRAME(_error) \ + Lucy_Err_Add_Frame(_error, __FILE__, __LINE__, \ + CFISH_ERR_FUNC_MACRO) + +#define CFISH_RETHROW(_error) \ + lucy_Err_rethrow((lucy_Err*)_error, __FILE__, __LINE__, \ + CFISH_ERR_FUNC_MACRO) + +/** Macro version of lucy_Err_throw_at which inserts contextual information + * automatically, provided that the compiler supports the necessary features. + */ +#ifdef CHY_HAS_VARIADIC_MACROS + #ifdef CHY_HAS_ISO_VARIADIC_MACROS + #define CFISH_THROW(_vtable, ...) \ + lucy_Err_throw_at(_vtable, __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO, \ + __VA_ARGS__) + #define CFISH_WARN(...) \ + lucy_Err_warn_at(__FILE__, __LINE__, CFISH_ERR_FUNC_MACRO, __VA_ARGS__) + #define CFISH_MAKE_MESS(...) \ + lucy_Err_make_mess(__FILE__, __LINE__, CFISH_ERR_FUNC_MACRO, \ + __VA_ARGS__) + #elif defined(CHY_HAS_GNUC_VARIADIC_MACROS) + #define CFISH_THROW(_vtable, args...) \ + lucy_Err_throw_at(_vtable, __FILE__, __LINE__, \ + CFISH_ERR_FUNC_MACRO, ##args) + #define CFISH_WARN(args...) \ + lucy_Err_warn_at(__FILE__, __LINE__, CFISH_ERR_FUNC_MACRO, ##args) + #define CFISH_MAKE_MESS(args...) \ + lucy_Err_make_mess(__FILE__, __LINE__, CFISH_ERR_FUNC_MACRO, ##args) + #endif +#else + void + CFISH_THROW(lucy_VTable *vtable, char* format, ...); + void + CFISH_WARN(char* format, ...); + lucy_CharBuf* + CFISH_MAKE_MESS(char* format, ...); +#endif + +#define CFISH_DOWNCAST(_obj, _vtable) \ + lucy_Err_downcast((lucy_Obj*)(_obj), (_vtable), \ + __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO) + + +#define CFISH_CERTIFY(_obj, _vtable) \ + lucy_Err_certify((lucy_Obj*)(_obj), (_vtable), \ + __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO) + +static CHY_INLINE void +lucy_Err_abstract_class_check(lucy_Obj *obj, lucy_VTable *vtable) { + lucy_VTable *const my_vtable = *(lucy_VTable**)obj; + if (my_vtable == vtable) { + lucy_CharBuf *mess = CFISH_MAKE_MESS("%o is an abstract class", + Lucy_Obj_Get_Class_Name(obj)); + Lucy_Obj_Dec_RefCount(obj); + lucy_Err_throw_mess(LUCY_ERR, mess); + } +} + +#define CFISH_ABSTRACT_CLASS_CHECK(_obj, _vtable) \ + lucy_Err_abstract_class_check(((lucy_Obj*)_obj), _vtable) + +#ifdef LUCY_USE_SHORT_NAMES + #define THROW CFISH_THROW + #define RETHROW CFISH_RETHROW + #define WARN CFISH_WARN + #define MAKE_MESS CFISH_MAKE_MESS + #define ERR_ADD_FRAME CFISH_ERR_ADD_FRAME + #define ERR_FUNC_MACRO CFISH_ERR_FUNC_MACRO + #define DOWNCAST CFISH_DOWNCAST + #define CERTIFY CFISH_CERTIFY + #define ABSTRACT_CLASS_CHECK CFISH_ABSTRACT_CLASS_CHECK +#endif +__END_C__ + + diff --git a/clownfish/core/Clownfish/Hash.c b/clownfish/core/Clownfish/Hash.c new file mode 100644 index 000000000..2f71e5fff --- /dev/null +++ b/clownfish/core/Clownfish/Hash.c @@ -0,0 +1,500 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_HASH +#define C_LUCY_HASHTOMBSTONE +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include +#include + +#include "Clownfish/VTable.h" + +#include "Clownfish/Hash.h" +#include "Clownfish/CharBuf.h" +#include "Clownfish/Err.h" +#include "Clownfish/VArray.h" +#include "Lucy/Store/InStream.h" +#include "Lucy/Store/OutStream.h" +#include "Lucy/Util/Freezer.h" +#include "Clownfish/Util/Memory.h" + +static HashTombStone *TOMBSTONE; + +#define HashEntry lucy_HashEntry + +typedef struct HashEntry { + Obj *key; + Obj *value; + int32_t hash_sum; +} HashEntry; + +// Reset the iterator. Hash_Iterate must be called to restart iteration. +static INLINE void +SI_kill_iter(Hash *self); + +// Return the entry associated with the key, if any. +static INLINE HashEntry* +SI_fetch_entry(Hash *self, const Obj *key, int32_t hash_sum); + +// Double the number of buckets and redistribute all entries. +static INLINE HashEntry* +SI_rebuild_hash(Hash *self); + +void +Hash_init_class() { + TOMBSTONE = (HashTombStone*)VTable_Make_Obj(HASHTOMBSTONE); +} + +Hash* +Hash_new(uint32_t capacity) { + Hash *self = (Hash*)VTable_Make_Obj(HASH); + return Hash_init(self, capacity); +} + +Hash* +Hash_init(Hash *self, uint32_t capacity) { + // Allocate enough space to hold the requested number of elements without + // triggering a rebuild. + uint32_t requested_capacity = capacity < I32_MAX ? capacity : I32_MAX; + uint32_t threshold; + capacity = 16; + while (1) { + threshold = (capacity / 3) * 2; + if (threshold > requested_capacity) { break; } + capacity *= 2; + } + + // Init. + self->size = 0; + self->iter_tick = -1; + + // Derive. + self->capacity = capacity; + self->entries = (HashEntry*)CALLOCATE(capacity, sizeof(HashEntry)); + self->threshold = threshold; + + return self; +} + +void +Hash_destroy(Hash *self) { + if (self->entries) { + Hash_Clear(self); + FREEMEM(self->entries); + } + SUPER_DESTROY(self, HASH); +} + +Hash* +Hash_dump(Hash *self) { + Hash *dump = Hash_new(self->size); + Obj *key; + Obj *value; + + Hash_Iterate(self); + while (Hash_Next(self, &key, &value)) { + // Since JSON only supports text hash keys, Dump() can only support + // text hash keys. + CERTIFY(key, CHARBUF); + Hash_Store(dump, key, Obj_Dump(value)); + } + + return dump; +} + +Obj* +Hash_load(Hash *self, Obj *dump) { + Hash *source = (Hash*)CERTIFY(dump, HASH); + CharBuf *class_name = (CharBuf*)Hash_Fetch_Str(source, "_class", 6); + UNUSED_VAR(self); + + // Assume that the presence of the "_class" key paired with a valid class + // name indicates the output of a Dump rather than an ordinary Hash. */ + if (class_name && CB_Is_A(class_name, CHARBUF)) { + VTable *vtable = VTable_fetch_vtable(class_name); + + if (!vtable) { + CharBuf *parent_class = VTable_find_parent_class(class_name); + if (parent_class) { + VTable *parent = VTable_singleton(parent_class, NULL); + vtable = VTable_singleton(class_name, parent); + DECREF(parent_class); + } + else { + // TODO: Fix Hash_Load() so that it works with ordinary hash + // keys named "_class". + THROW(ERR, "Can't find class '%o'", class_name); + } + } + + // Dispatch to an alternate Load() method. + if (vtable) { + Obj_Load_t load = METHOD_PTR(vtable, Lucy_Obj_Load); + if (load == Obj_load) { + THROW(ERR, "Abstract method Load() not defined for %o", + VTable_Get_Name(vtable)); + } + else if (load != (Obj_Load_t)Hash_load) { // stop inf loop + return VTable_Load_Obj(vtable, dump); + } + } + } + + // It's an ordinary Hash. + Hash *loaded = Hash_new(source->size); + Obj *key; + Obj *value; + Hash_Iterate(source); + while (Hash_Next(source, &key, &value)) { + Hash_Store(loaded, key, Obj_Load(value, value)); + } + + return (Obj*)loaded; + +} + +void +Hash_serialize(Hash *self, OutStream *outstream) { + Obj *key; + Obj *val; + uint32_t charbuf_count = 0; + OutStream_Write_C32(outstream, self->size); + + // Write CharBuf keys first. CharBuf keys are the common case; grouping + // them together is a form of run-length-encoding and saves space, since + // we omit the per-key class name. + Hash_Iterate(self); + while (Hash_Next(self, &key, &val)) { + if (Obj_Is_A(key, CHARBUF)) { charbuf_count++; } + } + OutStream_Write_C32(outstream, charbuf_count); + Hash_Iterate(self); + while (Hash_Next(self, &key, &val)) { + if (Obj_Is_A(key, CHARBUF)) { + Obj_Serialize(key, outstream); + FREEZE(val, outstream); + } + } + + // Punt on the classes of the remaining keys. + Hash_Iterate(self); + while (Hash_Next(self, &key, &val)) { + if (!Obj_Is_A(key, CHARBUF)) { + FREEZE(key, outstream); + FREEZE(val, outstream); + } + } +} + +Hash* +Hash_deserialize(Hash *self, InStream *instream) { + uint32_t size = InStream_Read_C32(instream); + uint32_t num_charbufs = InStream_Read_C32(instream); + uint32_t num_other = size - num_charbufs; + CharBuf *key = num_charbufs ? CB_new(0) : NULL; + + Hash_init(self, size); + + // Read key-value pairs with CharBuf keys. + while (num_charbufs--) { + uint32_t len = InStream_Read_C32(instream); + char *key_buf = CB_Grow(key, len); + InStream_Read_Bytes(instream, key_buf, len); + key_buf[len] = '\0'; + CB_Set_Size(key, len); + Hash_Store(self, (Obj*)key, THAW(instream)); + } + DECREF(key); + + // Read remaining key/value pairs. + while (num_other--) { + Obj *k = THAW(instream); + Hash_Store(self, k, THAW(instream)); + DECREF(k); + } + + return self; +} + +void +Hash_clear(Hash *self) { + HashEntry *entry = (HashEntry*)self->entries; + HashEntry *const limit = entry + self->capacity; + + // Iterate through all entries. + for (; entry < limit; entry++) { + if (!entry->key) { continue; } + DECREF(entry->key); + DECREF(entry->value); + entry->key = NULL; + entry->value = NULL; + entry->hash_sum = 0; + } + + self->size = 0; +} + +void +Hash_do_store(Hash *self, Obj *key, Obj *value, + int32_t hash_sum, bool_t use_this_key) { + HashEntry *entries = self->size >= self->threshold + ? SI_rebuild_hash(self) + : (HashEntry*)self->entries; + uint32_t tick = hash_sum; + const uint32_t mask = self->capacity - 1; + + while (1) { + tick &= mask; + HashEntry *entry = entries + tick; + if (entry->key == (Obj*)TOMBSTONE || !entry->key) { + if (entry->key == (Obj*)TOMBSTONE) { + // Take note of diminished tombstone clutter. + self->threshold++; + } + entry->key = use_this_key + ? key + : Hash_Make_Key(self, key, hash_sum); + entry->value = value; + entry->hash_sum = hash_sum; + self->size++; + break; + } + else if (entry->hash_sum == hash_sum + && Obj_Equals(key, entry->key) + ) { + DECREF(entry->value); + entry->value = value; + break; + } + tick++; // linear scan + } +} + +void +Hash_store(Hash *self, Obj *key, Obj *value) { + Hash_do_store(self, key, value, Obj_Hash_Sum(key), false); +} + +void +Hash_store_str(Hash *self, const char *key, size_t key_len, Obj *value) { + ZombieCharBuf *key_buf = ZCB_WRAP_STR((char*)key, key_len); + Hash_do_store(self, (Obj*)key_buf, value, + ZCB_Hash_Sum(key_buf), false); +} + +Obj* +Hash_make_key(Hash *self, Obj *key, int32_t hash_sum) { + UNUSED_VAR(self); + UNUSED_VAR(hash_sum); + return Obj_Clone(key); +} + +Obj* +Hash_fetch_str(Hash *self, const char *key, size_t key_len) { + ZombieCharBuf *key_buf = ZCB_WRAP_STR(key, key_len); + return Hash_fetch(self, (Obj*)key_buf); +} + +static INLINE HashEntry* +SI_fetch_entry(Hash *self, const Obj *key, int32_t hash_sum) { + uint32_t tick = hash_sum; + HashEntry *const entries = (HashEntry*)self->entries; + HashEntry *entry; + + while (1) { + tick &= self->capacity - 1; + entry = entries + tick; + if (!entry->key) { + // Failed to find the key, so return NULL. + return NULL; + } + else if (entry->hash_sum == hash_sum + && Obj_Equals(key, entry->key) + ) { + return entry; + } + tick++; + } +} + +Obj* +Hash_fetch(Hash *self, const Obj *key) { + HashEntry *entry = SI_fetch_entry(self, key, Obj_Hash_Sum(key)); + return entry ? entry->value : NULL; +} + +Obj* +Hash_delete(Hash *self, const Obj *key) { + HashEntry *entry = SI_fetch_entry(self, key, Obj_Hash_Sum(key)); + if (entry) { + Obj *value = entry->value; + DECREF(entry->key); + entry->key = (Obj*)TOMBSTONE; + entry->value = NULL; + entry->hash_sum = 0; + self->size--; + self->threshold--; // limit number of tombstones + return value; + } + else { + return NULL; + } +} + +Obj* +Hash_delete_str(Hash *self, const char *key, size_t key_len) { + ZombieCharBuf *key_buf = ZCB_WRAP_STR(key, key_len); + return Hash_delete(self, (Obj*)key_buf); +} + +uint32_t +Hash_iterate(Hash *self) { + SI_kill_iter(self); + return self->size; +} + +static INLINE void +SI_kill_iter(Hash *self) { + self->iter_tick = -1; +} + +bool_t +Hash_next(Hash *self, Obj **key, Obj **value) { + while (1) { + if (++self->iter_tick >= (int32_t)self->capacity) { + // Bail since we've completed the iteration. + --self->iter_tick; + *key = NULL; + *value = NULL; + return false; + } + else { + HashEntry *const entry + = (HashEntry*)self->entries + self->iter_tick; + if (entry->key && entry->key != (Obj*)TOMBSTONE) { + // Success! + *key = entry->key; + *value = entry->value; + return true; + } + } + } +} + +Obj* +Hash_find_key(Hash *self, const Obj *key, int32_t hash_sum) { + HashEntry *entry = SI_fetch_entry(self, key, hash_sum); + return entry ? entry->key : NULL; +} + +VArray* +Hash_keys(Hash *self) { + Obj *key; + Obj *val; + VArray *keys = VA_new(self->size); + Hash_Iterate(self); + while (Hash_Next(self, &key, &val)) { + VA_push(keys, INCREF(key)); + } + return keys; +} + +VArray* +Hash_values(Hash *self) { + Obj *key; + Obj *val; + VArray *values = VA_new(self->size); + Hash_Iterate(self); + while (Hash_Next(self, &key, &val)) { VA_push(values, INCREF(val)); } + return values; +} + +bool_t +Hash_equals(Hash *self, Obj *other) { + Hash *twin = (Hash*)other; + Obj *key; + Obj *val; + + if (twin == self) { return true; } + if (!Obj_Is_A(other, HASH)) { return false; } + if (self->size != twin->size) { return false; } + + Hash_Iterate(self); + while (Hash_Next(self, &key, &val)) { + Obj *other_val = Hash_Fetch(twin, key); + if (!other_val || !Obj_Equals(other_val, val)) { return false; } + } + + return true; +} + +uint32_t +Hash_get_capacity(Hash *self) { + return self->capacity; +} + +uint32_t +Hash_get_size(Hash *self) { + return self->size; +} + +static INLINE HashEntry* +SI_rebuild_hash(Hash *self) { + HashEntry *old_entries = (HashEntry*)self->entries; + HashEntry *entry = old_entries; + HashEntry *limit = old_entries + self->capacity; + + SI_kill_iter(self); + self->capacity *= 2; + self->threshold = (self->capacity / 3) * 2; + self->entries = (HashEntry*)CALLOCATE(self->capacity, sizeof(HashEntry)); + self->size = 0; + + for (; entry < limit; entry++) { + if (!entry->key || entry->key == (Obj*)TOMBSTONE) { + continue; + } + Hash_do_store(self, entry->key, entry->value, + entry->hash_sum, true); + } + + FREEMEM(old_entries); + + return (HashEntry*)self->entries; +} + +/***************************************************************************/ + +uint32_t +HashTombStone_get_refcount(HashTombStone* self) { + CHY_UNUSED_VAR(self); + return 1; +} + +HashTombStone* +HashTombStone_inc_refcount(HashTombStone* self) { + return self; +} + +uint32_t +HashTombStone_dec_refcount(HashTombStone* self) { + UNUSED_VAR(self); + return 1; +} + + diff --git a/clownfish/core/Clownfish/Hash.cfh b/clownfish/core/Clownfish/Hash.cfh new file mode 100644 index 000000000..a75be6390 --- /dev/null +++ b/clownfish/core/Clownfish/Hash.cfh @@ -0,0 +1,165 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** + * Hashtable. + * + * Values are stored by reference and may be any kind of Obj. By default, keys + * are cloned and so must belong to a class that implements Clone(); however, + * this behavior can be changed by overridding Make_Key(), e.g. to implement + * efficient hash sets. + */ +class Clownfish::Hash inherits Clownfish::Obj { + + void *entries; + uint32_t capacity; + uint32_t size; + uint32_t threshold; /* rehashing trigger point */ + int32_t iter_tick; /* used when iterating */ + + inert void + init_class(); + + inert incremented Hash* + new(uint32_t capacity = 0); + + /** + * @param capacity The number of elements that the hash will be asked to + * hold initially. + */ + inert Hash* + init(Hash *self, uint32_t capacity = 0); + + /** Empty the hash of all key-value pairs. + */ + void + Clear(Hash *self); + + /** Store a key-value pair. If key is not already present, + * Make_Key() will be called to manufacture the internally used key. + */ + void + Store(Hash *self, Obj *key, decremented Obj *value); + + void + Store_Str(Hash *self, const char *str, size_t len, + decremented Obj *value); + + /** Fetch the value associated with key. + * + * @return the value, or NULL if key is not present. + */ + nullable Obj* + Fetch(Hash *self, const Obj *key); + + nullable Obj* + Fetch_Str(Hash *self, const char *key, size_t key_len); + + /** Attempt to delete a key-value pair from the hash. + * + * @return the value if key exists and thus deletion + * succeeds; otherwise NULL. + */ + incremented nullable Obj* + Delete(Hash *self, const Obj *key); + + incremented nullable Obj* + Delete_Str(Hash *self, const char *key, size_t key_ley); + + /** Prepare to iterate over all the key-value pairs in the hash. + * + * @return the number of pairs which will be iterated over. + */ + uint32_t + Iterate(Hash *self); + + /** Retrieve the next key-value pair from the hash, setting the supplied + * pointers to point at them. + * + * @return true while iterating, false when the iterator has been + * exhausted. + */ + bool_t + Next(Hash *self, Obj **key, Obj **value); + + /** Search for a key which Equals the key supplied, and return the key + * rather than its value. + */ + nullable Obj* + Find_Key(Hash *self, const Obj *key, int32_t hash_sum); + + /** Return an VArray of pointers to the hash's keys. + */ + incremented VArray* + Keys(Hash *self); + + /** Return an VArray of pointers to the hash's values. + */ + incremented VArray* + Values(Hash *self); + + /** Create a key to be stored within the hash entry. Implementations must + * supply an object which produces the same Hash_Sum() value and tests + * true for Equals(). By default, calls Clone(). + */ + public incremented Obj* + Make_Key(Hash *self, Obj *key, int32_t hash_sum); + + uint32_t + Get_Capacity(Hash *self); + + /** Accessor for Hash's "size" member. + * + * @return the number of key-value pairs. + */ + public uint32_t + Get_Size(Hash *self); + + public bool_t + Equals(Hash *self, Obj *other); + + public incremented Hash* + Dump(Hash *self); + + public incremented Obj* + Load(Hash *self, Obj *dump); + + public void + Serialize(Hash *self, OutStream *outstream); + + public incremented Hash* + Deserialize(decremented Hash *self, InStream *instream); + + public void + Destroy(Hash *self); +} + +class Clownfish::Hash::HashTombStone + inherits Clownfish::Obj { + + uint32_t + Get_RefCount(HashTombStone* self); + + incremented HashTombStone* + Inc_RefCount(HashTombStone* self); + + uint32_t + Dec_RefCount(HashTombStone* self); +} + + diff --git a/clownfish/core/Clownfish/Host.cfh b/clownfish/core/Clownfish/Host.cfh new file mode 100644 index 000000000..203c6bbac --- /dev/null +++ b/clownfish/core/Clownfish/Host.cfh @@ -0,0 +1,108 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +__C__ +#define CFISH_HOST_ARGTYPE_I32 (int32_t)0x00000001 +#define CFISH_HOST_ARGTYPE_I64 (int32_t)0x00000002 +#define CFISH_HOST_ARGTYPE_F32 (int32_t)0x00000003 +#define CFISH_HOST_ARGTYPE_F64 (int32_t)0x00000004 +#define CFISH_HOST_ARGTYPE_STR (int32_t)0x00000006 +#define CFISH_HOST_ARGTYPE_OBJ (int32_t)0x00000007 +#define CFISH_HOST_ARGTYPE_MASK 0x00000007 + +#define CFISH_ARG_I32(_label, _value) \ + CFISH_HOST_ARGTYPE_I32, (_label), ((int32_t)_value) +#define CFISH_ARG_I64(_label, _value) \ + CFISH_HOST_ARGTYPE_I64, (_label), ((int64_t)_value) +#define CFISH_ARG_I(_type, _label, _value) \ + (sizeof(_type) <= 4 ? CFISH_HOST_ARGTYPE_I32 : CFISH_HOST_ARGTYPE_I64), \ + (_label), (sizeof(_type) <= 4 ? (int32_t)_value : (int64_t)_value) +#define CFISH_ARG_F32(_label, _value) \ + CFISH_HOST_ARGTYPE_F32, (_label), ((double)_value) +#define CFISH_ARG_F64(_label, _value) \ + CFISH_HOST_ARGTYPE_F64, (_label), ((double)_value) +#define CFISH_ARG_STR(_label, _value) \ + CFISH_HOST_ARGTYPE_STR, (_label), ((lucy_CharBuf*)_value) +#define CFISH_ARG_OBJ(_label, _value) \ + CFISH_HOST_ARGTYPE_OBJ, (_label), ((lucy_Obj*)_value) + +#ifdef LUCY_USE_SHORT_NAMES + #define ARG_I32 CFISH_ARG_I32 + #define ARG_I64 CFISH_ARG_I64 + #define ARG_F32 CFISH_ARG_F32 + #define ARG_F64 CFISH_ARG_F64 + #define ARG_STR CFISH_ARG_STR + #define ARG_OBJ CFISH_ARG_OBJ +#endif + +__END_C__ + +/** Callbacks to the host environment. + * + * All the callback functions are variadic, and all are designed to take a + * series of arguments using the ARG_XXX macros. + * + * int32_t area = (int32_t)Host_callback_i64(self, "calc_area", 2, + * ARG_I32("length", len), ARG_I32("width", width)); + * + * The first argument is void* to avoid the need for tiresome casting to Obj*, + * but must always be a Clownfish object. + * + * If the invoker is a VTable, it will be used to make a class + * callback rather than an object callback. + */ +inert class Clownfish::Host { + + /** Invoke an object method in a void context. + */ + inert void + callback(void *self, const char *method, uint32_t num_args, ...); + + /** Invoke an object method, expecting an integer. + */ + inert int64_t + callback_i64(void *self, const char *method, uint32_t num_args, ...); + + /** Invoke an object method, expecting a 64-bit floating point return + * value. + */ + inert double + callback_f64(void *self, const char *method, uint32_t num_args, ...); + + /** Invoke an object method, expecting a Obj-derived object back, or + * possibly NULL. In order to ensure that the host environment doesn't + * reclaim the return value, it's refcount is increased by one, which the + * caller will have to deal with. + */ + inert incremented nullable Obj* + callback_obj(void *self, const char *method, uint32_t num_args, ...); + + /** Invoke an object method, expecting a host string of some kind back, + * which will be converted into a newly allocated CharBuf. + */ + inert incremented nullable CharBuf* + callback_str(void *self, const char *method, uint32_t num_args, ...); + + /** Invoke an object method, expecting a host data structure back. It's + * up to the caller to know how to process it. + */ + inert nullable void* + callback_host(void *self, const char *method, uint32_t num_args, ...); +} + + diff --git a/clownfish/core/Clownfish/LockFreeRegistry.c b/clownfish/core/Clownfish/LockFreeRegistry.c new file mode 100644 index 000000000..e70f69d78 --- /dev/null +++ b/clownfish/core/Clownfish/LockFreeRegistry.c @@ -0,0 +1,129 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_LOCKFREEREGISTRY +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include "Clownfish/LockFreeRegistry.h" +#include "Clownfish/Err.h" +#include "Clownfish/VTable.h" +#include "Clownfish/Util/Atomic.h" +#include "Clownfish/Util/Memory.h" + +typedef struct lucy_LFRegEntry { + Obj *key; + Obj *value; + int32_t hash_sum; + struct lucy_LFRegEntry *volatile next; +} lucy_LFRegEntry; +#define LFRegEntry lucy_LFRegEntry + +LockFreeRegistry* +LFReg_new(size_t capacity) { + LockFreeRegistry *self + = (LockFreeRegistry*)VTable_Make_Obj(LOCKFREEREGISTRY); + return LFReg_init(self, capacity); +} + +LockFreeRegistry* +LFReg_init(LockFreeRegistry *self, size_t capacity) { + self->capacity = capacity; + self->entries = CALLOCATE(capacity, sizeof(void*)); + return self; +} + +bool_t +LFReg_register(LockFreeRegistry *self, Obj *key, Obj *value) { + LFRegEntry *new_entry = NULL; + int32_t hash_sum = Obj_Hash_Sum(key); + size_t bucket = (uint32_t)hash_sum % self->capacity; + LFRegEntry *volatile *entries = (LFRegEntry*volatile*)self->entries; + LFRegEntry *volatile *slot = &(entries[bucket]); + + // Proceed through the linked list. Bail out if the key has already been + // registered. +FIND_END_OF_LINKED_LIST: + while (*slot) { + LFRegEntry *entry = *slot; + if (entry->hash_sum == hash_sum) { + if (Obj_Equals(key, entry->key)) { + return false; + } + } + slot = &(entry->next); + } + + // We've found an empty slot. Create the new entry. + if (!new_entry) { + new_entry = (LFRegEntry*)MALLOCATE(sizeof(LFRegEntry)); + new_entry->hash_sum = hash_sum; + new_entry->key = INCREF(key); + new_entry->value = INCREF(value); + new_entry->next = NULL; + } + + /* Attempt to append the new node onto the end of the linked list. + * However, if another thread filled the slot since we found it (perhaps + * while we were allocating that new node), the compare-and-swap will + * fail. If that happens, we have to go back and find the new end of the + * linked list, then try again. */ + if (!Atomic_cas_ptr((void*volatile*)slot, NULL, new_entry)) { + goto FIND_END_OF_LINKED_LIST; + } + + return true; +} + +Obj* +LFReg_fetch(LockFreeRegistry *self, Obj *key) { + int32_t hash_sum = Obj_Hash_Sum(key); + size_t bucket = (uint32_t)hash_sum % self->capacity; + LFRegEntry **entries = (LFRegEntry**)self->entries; + LFRegEntry *entry = entries[bucket]; + + while (entry) { + if (entry->hash_sum == hash_sum) { + if (Obj_Equals(key, entry->key)) { + return entry->value; + } + } + entry = entry->next; + } + + return NULL; +} + +void +LFReg_destroy(LockFreeRegistry *self) { + LFRegEntry **entries = (LFRegEntry**)self->entries; + + for (size_t i = 0; i < self->capacity; i++) { + LFRegEntry *entry = entries[i]; + while (entry) { + LFRegEntry *next_entry = entry->next; + DECREF(entry->key); + DECREF(entry->value); + FREEMEM(entry); + entry = next_entry; + } + } + FREEMEM(self->entries); + + SUPER_DESTROY(self, LOCKFREEREGISTRY); +} + + diff --git a/clownfish/core/Clownfish/LockFreeRegistry.cfh b/clownfish/core/Clownfish/LockFreeRegistry.cfh new file mode 100644 index 000000000..a801011e7 --- /dev/null +++ b/clownfish/core/Clownfish/LockFreeRegistry.cfh @@ -0,0 +1,45 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** Specialized lock free hash table for storing VTables. + */ +class Clownfish::LockFreeRegistry cnick LFReg inherits Clownfish::Obj { + + size_t capacity; + void *entries; + + inert incremented LockFreeRegistry* + new(size_t capacity); + + inert LockFreeRegistry* + init(LockFreeRegistry *self, size_t capacity); + + public void + Destroy(LockFreeRegistry *self); + + bool_t + Register(LockFreeRegistry *self, Obj *key, Obj *value); + + nullable Obj* + Fetch(LockFreeRegistry *self, Obj *key); + + void* + To_Host(LockFreeRegistry *self); +} + + diff --git a/clownfish/core/Clownfish/Method.c b/clownfish/core/Clownfish/Method.c new file mode 100644 index 000000000..13f3abbbf --- /dev/null +++ b/clownfish/core/Clownfish/Method.c @@ -0,0 +1,64 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_METHOD +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include "Clownfish/Method.h" +#include "Clownfish/CharBuf.h" +#include "Clownfish/Err.h" +#include "Clownfish/VTable.h" + +Method* +Method_new(const CharBuf *name, lucy_method_t callback_func, size_t offset) { + Method *self = (Method*)VTable_Make_Obj(METHOD); + return Method_init(self, name, callback_func, offset); +} + +Method* +Method_init(Method *self, const CharBuf *name, lucy_method_t callback_func, + size_t offset) { + self->name = CB_Clone(name); + self->callback_func = callback_func; + self->offset = offset; + return self; +} + +void +Method_destroy(Method *self) { + THROW(ERR, "Insane attempt to destroy Method '%o'", self->name); +} + +Obj* +Method_inc_refcount(Method *self) { + return (Obj*)self; +} + +uint32_t +Method_dec_refcount(Method *self) { + UNUSED_VAR(self); + return 1; +} + +uint32_t +Method_get_refcount(Method *self) { + UNUSED_VAR(self); + // See comments in VTable.c + return 1; +} + + diff --git a/clownfish/core/Clownfish/Method.cfh b/clownfish/core/Clownfish/Method.cfh new file mode 100644 index 000000000..b2476ec89 --- /dev/null +++ b/clownfish/core/Clownfish/Method.cfh @@ -0,0 +1,48 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** Method metadata. + */ + +class Clownfish::Method inherits Clownfish::Obj { + + CharBuf *name; + lucy_method_t callback_func; + size_t offset; + + inert Method* + new(const CharBuf *name, lucy_method_t callback_func, size_t offset); + + inert Method* + init(Method *self, const CharBuf *name, lucy_method_t callback_func, + size_t offset); + + incremented Obj* + Inc_RefCount(Method *self); + + uint32_t + Dec_RefCount(Method *self); + + uint32_t + Get_RefCount(Method *self); + + public void + Destroy(Method *self); +} + + diff --git a/clownfish/core/Clownfish/Num.c b/clownfish/core/Clownfish/Num.c new file mode 100644 index 000000000..e01f96df6 --- /dev/null +++ b/clownfish/core/Clownfish/Num.c @@ -0,0 +1,456 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_NUM +#define C_LUCY_INTNUM +#define C_LUCY_FLOATNUM +#define C_LUCY_INTEGER32 +#define C_LUCY_INTEGER64 +#define C_LUCY_FLOAT32 +#define C_LUCY_FLOAT64 +#define C_LUCY_BOOLNUM +#define C_LUCY_VIEWCHARBUF +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include "Clownfish/Num.h" +#include "Clownfish/CharBuf.h" +#include "Clownfish/Err.h" +#include "Clownfish/VTable.h" +#include "Lucy/Store/InStream.h" +#include "Lucy/Store/OutStream.h" + +Num* +Num_init(Num *self) { + ABSTRACT_CLASS_CHECK(self, NUM); + return self; +} + +bool_t +Num_equals(Num *self, Obj *other) { + Num *twin = (Num*)other; + if (twin == self) { return true; } + if (!Obj_Is_A(other, NUM)) { return false; } + if (Num_To_F64(self) != Num_To_F64(twin)) { return false; } + if (Num_To_I64(self) != Num_To_I64(twin)) { return false; } + return true; +} + +/***************************************************************************/ + +FloatNum* +FloatNum_init(FloatNum *self) { + ABSTRACT_CLASS_CHECK(self, FLOATNUM); + return (FloatNum*)Num_init((Num*)self); +} + +int32_t +FloatNum_compare_to(FloatNum *self, Obj *other) { + Num *twin = (Num*)CERTIFY(other, NUM); + double f64_diff = FloatNum_To_F64(self) - Num_To_F64(twin); + if (f64_diff < 0) { return -1; } + else if (f64_diff > 0) { return 1; } + return 0; +} + +CharBuf* +FloatNum_to_string(FloatNum *self) { + return CB_newf("%f64", FloatNum_To_F64(self)); +} + +/***************************************************************************/ + +IntNum* +IntNum_init(IntNum *self) { + ABSTRACT_CLASS_CHECK(self, INTNUM); + return (IntNum*)Num_init((Num*)self); +} + +int32_t +IntNum_compare_to(IntNum *self, Obj *other) { + if (!Obj_Is_A(other, INTNUM)) { + return -Obj_Compare_To(other, (Obj*)self); + } + int64_t self_value = IntNum_To_I64(self); + int64_t other_value = Obj_To_I64(other); + if (self_value < other_value) { return -1; } + else if (self_value > other_value) { return 1; } + return 0; +} + +CharBuf* +IntNum_to_string(IntNum *self) { + return CB_newf("%i64", IntNum_To_I64(self)); +} + +/***************************************************************************/ + +Float32* +Float32_new(float value) { + Float32 *self = (Float32*)VTable_Make_Obj(FLOAT32); + return Float32_init(self, value); +} + +Float32* +Float32_init(Float32 *self, float value) { + self->value = value; + return (Float32*)FloatNum_init((FloatNum*)self); +} + +float +Float32_get_value(Float32 *self) { + return self->value; +} + +void +Float32_set_value(Float32 *self, float value) { + self->value = value; +} + +double +Float32_to_f64(Float32 *self) { + return self->value; +} + +int64_t +Float32_to_i64(Float32 *self) { + return (int64_t)self->value; +} + +int32_t +Float32_hash_sum(Float32 *self) { + return *(int32_t*)&self->value; +} + +Float32* +Float32_clone(Float32 *self) { + return Float32_new(self->value); +} + +void +Float32_mimic(Float32 *self, Obj *other) { + Float32 *twin = (Float32*)CERTIFY(other, FLOAT32); + self->value = twin->value; +} + +void +Float32_serialize(Float32 *self, OutStream *outstream) { + OutStream_Write_F32(outstream, self->value); +} + +Float32* +Float32_deserialize(Float32 *self, InStream *instream) { + float value = InStream_Read_F32(instream); + return Float32_init(self, value); +} + +/***************************************************************************/ + +Float64* +Float64_new(double value) { + Float64 *self = (Float64*)VTable_Make_Obj(FLOAT64); + return Float64_init(self, value); +} + +Float64* +Float64_init(Float64 *self, double value) { + self->value = value; + return (Float64*)FloatNum_init((FloatNum*)self); +} + +double +Float64_get_value(Float64 *self) { + return self->value; +} + +void +Float64_set_value(Float64 *self, double value) { + self->value = value; +} + +double +Float64_to_f64(Float64 *self) { + return self->value; +} + +int64_t +Float64_to_i64(Float64 *self) { + return (int64_t)self->value; +} + +Float64* +Float64_clone(Float64 *self) { + return Float64_new(self->value); +} + +void +Float64_mimic(Float64 *self, Obj *other) { + Float64 *twin = (Float64*)CERTIFY(other, FLOAT64); + self->value = twin->value; +} + +int32_t +Float64_hash_sum(Float64 *self) { + int32_t *ints = (int32_t*)&self->value; + return ints[0] ^ ints[1]; +} + +void +Float64_serialize(Float64 *self, OutStream *outstream) { + OutStream_Write_F64(outstream, self->value); +} + +Float64* +Float64_deserialize(Float64 *self, InStream *instream) { + double value = InStream_Read_F64(instream); + return Float64_init(self, value); +} + +/***************************************************************************/ + +Integer32* +Int32_new(int32_t value) { + Integer32 *self = (Integer32*)VTable_Make_Obj(INTEGER32); + return Int32_init(self, value); +} + +Integer32* +Int32_init(Integer32 *self, int32_t value) { + self->value = value; + return (Integer32*)IntNum_init((IntNum*)self); +} + +int32_t +Int32_get_value(Integer32 *self) { + return self->value; +} + +void +Int32_set_value(Integer32 *self, int32_t value) { + self->value = value; +} + +double +Int32_to_f64(Integer32 *self) { + return self->value; +} + +int64_t +Int32_to_i64(Integer32 *self) { + return self->value; +} + +Integer32* +Int32_clone(Integer32 *self) { + return Int32_new(self->value); +} + +void +Int32_mimic(Integer32 *self, Obj *other) { + Integer32 *twin = (Integer32*)CERTIFY(other, INTEGER32); + self->value = twin->value; +} + +int32_t +Int32_hash_sum(Integer32 *self) { + return self->value; +} + +void +Int32_serialize(Integer32 *self, OutStream *outstream) { + OutStream_Write_C32(outstream, (uint32_t)self->value); +} + +Integer32* +Int32_deserialize(Integer32 *self, InStream *instream) { + int32_t value = (int32_t)InStream_Read_C32(instream); + return Int32_init(self, value); +} + +/***************************************************************************/ + +Integer64* +Int64_new(int64_t value) { + Integer64 *self = (Integer64*)VTable_Make_Obj(INTEGER64); + return Int64_init(self, value); +} + +Integer64* +Int64_init(Integer64 *self, int64_t value) { + self->value = value; + return (Integer64*)IntNum_init((IntNum*)self); +} + +int64_t +Int64_get_value(Integer64 *self) { + return self->value; +} + +void +Int64_set_value(Integer64 *self, int64_t value) { + self->value = value; +} + +double +Int64_to_f64(Integer64 *self) { + return (double)self->value; +} + +int64_t +Int64_to_i64(Integer64 *self) { + return self->value; +} + +Integer64* +Int64_clone(Integer64 *self) { + return Int64_new(self->value); +} + +void +Int64_mimic(Integer64 *self, Obj *other) { + Integer64 *twin = (Integer64*)CERTIFY(other, INTEGER64); + self->value = twin->value; +} + +int32_t +Int64_hash_sum(Integer64 *self) { + int32_t *ints = (int32_t*)&self->value; + return ints[0] ^ ints[1]; +} + +bool_t +Int64_equals(Integer64 *self, Obj *other) { + Num *twin = (Num*)other; + if (twin == (Num*)self) { return true; } + if (!Obj_Is_A(other, NUM)) { return false; } + if (Num_Is_A(twin, FLOATNUM)) { + double floating_val = Num_To_F64(twin); + int64_t int_val = (int64_t)floating_val; + if ((double)int_val != floating_val) { return false; } + if (int_val != self->value) { return false; } + } + else { + if (self->value != Num_To_I64(twin)) { return false; } + } + return true; +} + +void +Int64_serialize(Integer64 *self, OutStream *outstream) { + OutStream_Write_C64(outstream, (uint64_t)self->value); +} + +Integer64* +Int64_deserialize(Integer64 *self, InStream *instream) { + int64_t value = (int64_t)InStream_Read_C64(instream); + return Int64_init(self, value); +} + +/***************************************************************************/ + + +BoolNum *Bool_true_singleton; +BoolNum *Bool_false_singleton; + +void +Bool_init_class() { + Bool_true_singleton = (BoolNum*)VTable_Make_Obj(BOOLNUM); + Bool_true_singleton->value = true; + Bool_true_singleton->string = CB_newf("true"); + Bool_false_singleton = (BoolNum*)VTable_Make_Obj(BOOLNUM); + Bool_false_singleton->value = false; + Bool_false_singleton->string = CB_newf("false"); +} + +BoolNum* +Bool_singleton(bool_t value) { + return value ? CFISH_TRUE : CFISH_FALSE; +} + +void +Bool_destroy(BoolNum *self) { + if (self && self != CFISH_TRUE && self != CFISH_FALSE) { + SUPER_DESTROY(self, BOOLNUM); + } +} + +bool_t +Bool_get_value(BoolNum *self) { + return self->value; +} + +double +Bool_to_f64(BoolNum *self) { + return (double)self->value; +} + +int64_t +Bool_to_i64(BoolNum *self) { + return self->value; +} + +bool_t +Bool_to_bool(BoolNum *self) { + return self->value; +} + +BoolNum* +Bool_clone(BoolNum *self) { + return self; +} + +int32_t +Bool_hash_sum(BoolNum *self) { + int64_t hash_sum = PTR_TO_I64(self) + self->value; + return (int32_t)hash_sum; +} + +CharBuf* +Bool_to_string(BoolNum *self) { + return (CharBuf*)CB_Inc_RefCount(self->string); +} + +bool_t +Bool_equals(BoolNum *self, Obj *other) { + return self == (BoolNum*)other; +} + +void +Bool_serialize(BoolNum *self, OutStream *outstream) { + OutStream_Write_U8(outstream, (uint8_t)self->value); +} + +BoolNum* +Bool_deserialize(BoolNum *self, InStream *instream) { + bool_t value = (bool_t)InStream_Read_U8(instream); + if (self && self != CFISH_TRUE && self != CFISH_FALSE) { + Bool_Dec_RefCount_t super_decref + = SUPER_METHOD_PTR(BOOLNUM, Lucy_Bool_Dec_RefCount); + super_decref(self); + } + return value ? CFISH_TRUE : CFISH_FALSE; +} + +BoolNum* +Bool_inc_refcount(BoolNum *self) { + return self; +} + +uint32_t +Bool_dec_refcount(BoolNum *self) { + return 1; +} + diff --git a/clownfish/core/Clownfish/Num.cfh b/clownfish/core/Clownfish/Num.cfh new file mode 100644 index 000000000..b702758dd --- /dev/null +++ b/clownfish/core/Clownfish/Num.cfh @@ -0,0 +1,307 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** Abstract base class for numbers. + */ +abstract class Clownfish::Num inherits Clownfish::Obj { + + inert Num* + init(Num *self); + + public bool_t + Equals(Num *self, Obj *other); +} + +/** Abstract base class for floating point numbers. + */ +abstract class Clownfish::FloatNum inherits Clownfish::Num { + + inert FloatNum* + init(FloatNum *self); + + public int32_t + Compare_To(FloatNum *self, Obj *other); + + public incremented CharBuf* + To_String(FloatNum *self); +} + + +/** Abstract base class for Integers. + */ +abstract class Clownfish::IntNum inherits Clownfish::Num { + + inert IntNum* + init(IntNum *self); + + public int32_t + Compare_To(IntNum *self, Obj *other); + + public incremented CharBuf* + To_String(IntNum *self); +} + +/** Single precision floating point number. + */ +class Clownfish::Float32 inherits Clownfish::FloatNum { + + float value; + + /** + * @param value Initial value. + */ + inert Float32* + init(Float32* self, float value); + + inert Float32* + new(float value); + + void + Set_Value(Float32 *self, float value); + + float + Get_Value(Float32 *self); + + public int64_t + To_I64(Float32 *self); + + public double + To_F64(Float32 *self); + + public int32_t + Hash_Sum(Float32 *self); + + public void + Serialize(Float32 *self, OutStream *outstream); + + public incremented Float32* + Deserialize(decremented Float32 *self, InStream *instream); + + public incremented Float32* + Clone(Float32 *self); + + public void + Mimic(Float32 *self, Obj *other); +} + +/** Double precision floating point number. + */ +class Clownfish::Float64 inherits Clownfish::FloatNum { + + double value; + + /** + * @param value Initial value. + */ + inert Float64* + init(Float64* self, double value); + + inert Float64* + new(double value); + + void + Set_Value(Float64 *self, double value); + + double + Get_Value(Float64 *self); + + public int64_t + To_I64(Float64 *self); + + public double + To_F64(Float64 *self); + + public int32_t + Hash_Sum(Float64 *self); + + public void + Serialize(Float64 *self, OutStream *outstream); + + public incremented Float64* + Deserialize(decremented Float64 *self, InStream *instream); + + public incremented Float64* + Clone(Float64 *self); + + public void + Mimic(Float64 *self, Obj *other); +} + +/** 32-bit signed integer. + */ +class Clownfish::Integer32 cnick Int32 + inherits Clownfish::IntNum { + + int32_t value; + + /** + * @param value Initial value. + */ + inert Integer32* + init(Integer32* self, int32_t value); + + inert Integer32* + new(int32_t value); + + void + Set_Value(Integer32 *self, int32_t value); + + int32_t + Get_Value(Integer32 *self); + + public int64_t + To_I64(Integer32 *self); + + public double + To_F64(Integer32 *self); + + public int32_t + Hash_Sum(Integer32 *self); + + public void + Serialize(Integer32 *self, OutStream *outstream); + + public incremented Integer32* + Deserialize(decremented Integer32 *self, InStream *instream); + + public incremented Integer32* + Clone(Integer32 *self); + + public void + Mimic(Integer32 *self, Obj *other); +} + +/** + * 64-bit signed integer. + */ +class Clownfish::Integer64 cnick Int64 + inherits Clownfish::IntNum { + + int64_t value; + + /** + * @param value Initial value. + */ + inert Integer64* + init(Integer64* self, int64_t value); + + inert Integer64* + new(int64_t value); + + void + Set_Value(Integer64 *self, int64_t value); + + int64_t + Get_Value(Integer64 *self); + + public int64_t + To_I64(Integer64 *self); + + public double + To_F64(Integer64 *self); + + public int32_t + Hash_Sum(Integer64 *self); + + public bool_t + Equals(Integer64 *self, Obj *other); + + public void + Serialize(Integer64 *self, OutStream *outstream); + + public incremented Integer64* + Deserialize(decremented Integer64 *self, InStream *instream); + + public incremented Integer64* + Clone(Integer64 *self); + + public void + Mimic(Integer64 *self, Obj *other); +} + + +/** + * Boolean type. + * + * There are only two singleton instances of this class: CFISH_TRUE and + * CFISH_FALSE. + */ +class Clownfish::BoolNum cnick Bool inherits Clownfish::IntNum { + bool_t value; + CharBuf *string; + + inert BoolNum *true_singleton; + inert BoolNum *false_singleton; + + inert void + init_class(); + + /** Return either CFISH_TRUE or CFISH_FALSE depending on the supplied + * value. + */ + inert BoolNum* + singleton(bool_t value); + + public void + Destroy(BoolNum *self); + + bool_t + Get_Value(BoolNum *self); + + public int64_t + To_I64(BoolNum *self); + + public double + To_F64(BoolNum *self); + + public bool_t + To_Bool(BoolNum *self); + + public int32_t + Hash_Sum(BoolNum *self); + + public void + Serialize(BoolNum *self, OutStream *outstream); + + public incremented BoolNum* + Deserialize(decremented BoolNum *self, InStream *instream); + + /* Returns self. */ + public incremented BoolNum* + Clone(BoolNum *self); + + public bool_t + Equals(BoolNum *self, Obj *other); + + public incremented CharBuf* + To_String(BoolNum *self); + + incremented BoolNum* + Inc_RefCount(BoolNum *self); + + uint32_t + Dec_RefCount(BoolNum *self); +} + +__C__ + +#define CFISH_TRUE lucy_Bool_true_singleton +#define CFISH_FALSE lucy_Bool_false_singleton + +__END_C__ + diff --git a/clownfish/core/Clownfish/Obj.c b/clownfish/core/Clownfish/Obj.c new file mode 100644 index 000000000..7b44435e3 --- /dev/null +++ b/clownfish/core/Clownfish/Obj.c @@ -0,0 +1,125 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_OBJ +#define C_LUCY_VTABLE +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include +#include +#include + +#include "Clownfish/Obj.h" +#include "Clownfish/CharBuf.h" +#include "Clownfish/Err.h" +#include "Clownfish/Hash.h" +#include "Clownfish/VTable.h" +#include "Lucy/Store/InStream.h" +#include "Lucy/Store/OutStream.h" +#include "Clownfish/Util/Memory.h" + +Obj* +Obj_init(Obj *self) { + ABSTRACT_CLASS_CHECK(self, OBJ); + return self; +} + +void +Obj_destroy(Obj *self) { + FREEMEM(self); +} + +int32_t +Obj_hash_sum(Obj *self) { + int64_t hash_sum = PTR_TO_I64(self); + return (int32_t)hash_sum; +} + +bool_t +Obj_is_a(Obj *self, VTable *ancestor) { + VTable *vtable = self ? self->vtable : NULL; + + while (vtable != NULL) { + if (vtable == ancestor) { + return true; + } + vtable = vtable->parent; + } + + return false; +} + +bool_t +Obj_equals(Obj *self, Obj *other) { + return (self == other); +} + +void +Obj_serialize(Obj *self, OutStream *outstream) { + CharBuf *class_name = Obj_Get_Class_Name(self); + CB_Serialize(class_name, outstream); +} + +Obj* +Obj_deserialize(Obj *self, InStream *instream) { + CharBuf *class_name + = CB_Deserialize((CharBuf*)VTable_Make_Obj(CHARBUF), instream); + CharBuf *my_class = VTable_Get_Name(self->vtable); + if (!CB_Equals(class_name, (Obj*)my_class)) { + THROW(ERR, "Class mismatch: %o %o", class_name, my_class); + } + DECREF(class_name); + return Obj_init(self); +} + +CharBuf* +Obj_to_string(Obj *self) { +#if (SIZEOF_PTR == 4) + return CB_newf("%o@0x%x32", Obj_Get_Class_Name(self), self); +#elif (SIZEOF_PTR == 8) + int64_t iaddress = PTR_TO_I64(self); + uint64_t address = (uint64_t)iaddress; + uint32_t address_hi = address >> 32; + uint32_t address_lo = address & 0xFFFFFFFF; + return CB_newf("%o@0x%x32%x32", Obj_Get_Class_Name(self), address_hi, + address_lo); +#else + #error "Unexpected pointer size." +#endif +} + +bool_t +Obj_to_bool(Obj *self) { + return !!Obj_To_I64(self); +} + +Obj* +Obj_dump(Obj *self) { + return (Obj*)Obj_To_String(self); +} + +VTable* +Obj_get_vtable(Obj *self) { + return self->vtable; +} + +CharBuf* +Obj_get_class_name(Obj *self) { + return VTable_Get_Name(self->vtable); +} + + diff --git a/clownfish/core/Clownfish/Obj.cfh b/clownfish/core/Clownfish/Obj.cfh new file mode 100644 index 000000000..de57b2e4f --- /dev/null +++ b/clownfish/core/Clownfish/Obj.cfh @@ -0,0 +1,225 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** Base class for all objects. + */ + +public class Clownfish::Obj { + + VTable *vtable; + lucy_ref_t ref; + + /** Abstract initializer. + */ + public inert Obj* + init(Obj* self); + + /** Zero argument factory constructor. + */ + public abstract incremented Obj* + Make(Obj *self); + + /** Return an object's refcount. + */ + uint32_t + Get_RefCount(Obj *self); + + /** Increment an object's refcount. + * + * @return The object, allowing an assignment idiom. + */ + incremented Obj* + Inc_RefCount(Obj *self); + + /** NULL-safe invocation of Inc_RefCount(). + * + * @return NULL if self is NULL, otherwise the return value + * of Inc_RefCount(). + */ + inert inline incremented Obj* + incref(Obj *self); + + /** Decrement an object's refcount, calling Destroy() if it hits 0. + * + * @return the modified refcount. + */ + uint32_t + Dec_RefCount(Obj *self); + + /** NULL-safe invocation of Dec_RefCount(). + * + * @return NULL if self is NULL, otherwise the return value + * of Dec_RefCount(). + */ + inert inline uint32_t + decref(Obj *self); + + /** Return a host-language object wrapper for this object. + */ + void* + To_Host(Obj *self); + + /** Return a clone of the object. + */ + public abstract incremented Obj* + Clone(Obj *self); + + /** Generic destructor. Frees the struct itself but not any complex + * member elements. + */ + public void + Destroy(Obj *self); + + /** Invoke the Destroy() method found in vtable on + * self. + * + * TODO: Eliminate this function if we can arrive at a proper SUPER syntax. + */ + inert inline void + super_destroy(Obj *self, VTable *vtable); + + /** Indicate whether two objects are the same. By default, compares the + * memory address. + * + * @param other Another Obj. + */ + public bool_t + Equals(Obj *self, Obj *other); + + /** Indicate whether one object is less than, equal to, or greater than + * another. + * + * @param other Another Obj. + * @return 0 if the objects are equal, a negative number if + * self is less than other, and a positive + * number if self is greater than other. + */ + public abstract int32_t + Compare_To(Obj *self, Obj *other); + + /** Return a hash code for the object -- by default, the memory address. + */ + public int32_t + Hash_Sum(Obj *self); + + /** Return the object's VTable. + */ + public VTable* + Get_VTable(Obj *self); + + /** Return the name of the class that the object belongs to. + */ + public CharBuf* + Get_Class_Name(Obj *self); + + /** Indicate whether the object is a descendent of ancestor. + */ + public bool_t + Is_A(Obj *self, VTable *ancestor); + + /** Generic stringification: "ClassName@hex_mem_address". + */ + public incremented CharBuf* + To_String(Obj *self); + + /** Convert the object to a 64-bit integer. + */ + public abstract int64_t + To_I64(Obj *self); + + /** Convert the object to a double precision floating point number. + */ + public abstract double + To_F64(Obj *self); + + /** Evaluate the object in a boolean context. By default, invokes + * To_I64() and returns true if it is non-zero. + */ + public bool_t + To_Bool(Obj *self); + + /** Serialize the object by writing to the supplied OutStream. + */ + public void + Serialize(Obj *self, OutStream *outstream); + + /** Inflate an object by reading the serialized form from the instream. + * The assumption is that the object has been allocated, assigned a + * refcount and a vtable, but that everything else is uninitialized. + */ + public incremented Obj* + Deserialize(decremented Obj *self, InStream *instream); + + /** Return a representation of the object using only scalars, hashes, and + * arrays. Some implementations support JSON serialization via Dump() and + * its companion method, Load(); for others, Dump() is only a debugging + * aid. The default simply calls To_String(). + */ + public incremented Obj* + Dump(Obj *self); + + /** Create an object from the output of a call to Dump(). + * + * @param dump The output of Dump(). + */ + public abstract incremented Obj* + Load(Obj *self, Obj *dump); + + /** Update the internal state of the object to mimic that of + * other. + */ + public abstract void + Mimic(Obj *self, Obj *other); +} + +__C__ +static CHY_INLINE void +lucy_Obj_super_destroy(lucy_Obj *self, lucy_VTable *vtable) { + Lucy_Obj_Destroy_t super_destroy + = CFISH_SUPER_METHOD_PTR(vtable, Lucy_Obj_Destroy); + super_destroy(self); +} + +#define LUCY_SUPER_DESTROY(_self, _vtable) \ + lucy_Obj_super_destroy((lucy_Obj*)_self, _vtable) + +static CHY_INLINE lucy_Obj* +lucy_Obj_incref(lucy_Obj *self) { + if (self != NULL) { return Lucy_Obj_Inc_RefCount(self); } + else { return NULL; } +} + +#define CFISH_INCREF(_self) lucy_Obj_incref((lucy_Obj*)_self) + +static CHY_INLINE uint32_t +lucy_Obj_decref(lucy_Obj *self) { + if (self != NULL) { return Lucy_Obj_Dec_RefCount(self); } + else { return 0; } +} + +#define CFISH_DECREF(_self) lucy_Obj_decref((lucy_Obj*)_self) + +#ifdef LUCY_USE_SHORT_NAMES + #define SUPER_DESTROY(_self, _vtable) LUCY_SUPER_DESTROY(_self, _vtable) + #define INCREF(_self) CFISH_INCREF(_self) + #define DECREF(_self) CFISH_DECREF(_self) +#endif + +__END_C__ + + diff --git a/clownfish/core/Clownfish/Util/Atomic.c b/clownfish/core/Clownfish/Util/Atomic.c new file mode 100644 index 000000000..957522ec3 --- /dev/null +++ b/clownfish/core/Clownfish/Util/Atomic.c @@ -0,0 +1,40 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_ATOMIC +#define LUCY_USE_SHORT_NAMES +#include "Clownfish/Util/Atomic.h" + +/********************************** Windows ********************************/ +#ifdef CHY_HAS_WINDOWS_H +#include + +chy_bool_t +lucy_Atomic_wrapped_cas_ptr(void *volatile *target, void *old_value, + void *new_value) { + return InterlockedCompareExchangePointer(target, new_value, old_value) + == old_value; +} + +/************************** Fall back to ptheads ***************************/ +#elif defined(CHY_HAS_PTHREAD_H) + +#include +pthread_mutex_t lucy_Atomic_mutex = PTHREAD_MUTEX_INITIALIZER; + +#endif + + diff --git a/clownfish/core/Clownfish/Util/Atomic.cfh b/clownfish/core/Clownfish/Util/Atomic.cfh new file mode 100644 index 000000000..c673c1c65 --- /dev/null +++ b/clownfish/core/Clownfish/Util/Atomic.cfh @@ -0,0 +1,109 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** Provide atomic memory operations. + */ +inert class Clownfish::Util::Atomic { } + +__C__ + +/** Compare and swap a pointer. Test whether the value at target + * matches old_value. If it does, set target to + * new_value and return true. Otherwise, return false. + */ +static CHY_INLINE chy_bool_t +lucy_Atomic_cas_ptr(void *volatile *target, void *old_value, void *new_value); + +/************************** Single threaded *******************************/ +#ifdef LUCY_NOTHREADS + +static CHY_INLINE chy_bool_t +lucy_Atomic_cas_ptr(void *volatile *target, void *old_value, void *new_value) { + if (*target == old_value) { + *target = new_value; + return true; + } + else { + return false; + } +} + +/************************** Mac OS X 10.4 and later ***********************/ +#elif defined(CHY_HAS_OSATOMIC_CAS_PTR) +#include + +static CHY_INLINE chy_bool_t +lucy_Atomic_cas_ptr(void *volatile *target, void *old_value, void *new_value) { + return OSAtomicCompareAndSwapPtr(old_value, new_value, target); +} + +/********************************** Windows *******************************/ +#elif defined(CHY_HAS_WINDOWS_H) + +chy_bool_t +lucy_Atomic_wrapped_cas_ptr(void *volatile *target, void *old_value, + void *new_value); + +static CHY_INLINE chy_bool_t +lucy_Atomic_cas_ptr(void *volatile *target, void *old_value, void *new_value) { + return lucy_Atomic_wrapped_cas_ptr(target, old_value, new_value); +} + +/**************************** Solaris 10 and later ************************/ +#elif defined(CHY_HAS_SYS_ATOMIC_H) +#include + +static CHY_INLINE chy_bool_t +lucy_Atomic_cas_ptr(void *volatile *target, void *old_value, void *new_value) { + return atomic_cas_ptr(target, old_value, new_value) == old_value; +} + +/************************ Fall back to pthread.h. **************************/ +#elif defined(CHY_HAS_PTHREAD_H) +#include + +extern pthread_mutex_t lucy_Atomic_mutex; + +static CHY_INLINE chy_bool_t +lucy_Atomic_cas_ptr(void *volatile *target, void *old_value, void *new_value) { + pthread_mutex_lock(&lucy_Atomic_mutex); + if (*target == old_value) { + *target = new_value; + pthread_mutex_unlock(&lucy_Atomic_mutex); + return true; + } + else { + pthread_mutex_unlock(&lucy_Atomic_mutex); + return false; + } +} + +/******************** No support for atomics at all. ***********************/ +#else + +#error "No support for atomic operations." + +#endif // Big platform if-else chain. + +#ifdef LUCY_USE_SHORT_NAMES + #define Atomic_cas_ptr lucy_Atomic_cas_ptr +#endif + +__END_C__ + + diff --git a/clownfish/core/Clownfish/Util/Memory.c b/clownfish/core/Clownfish/Util/Memory.c new file mode 100644 index 000000000..c2a9086f5 --- /dev/null +++ b/clownfish/core/Clownfish/Util/Memory.c @@ -0,0 +1,112 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_MEMORY +#include +#include +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES +#include "Clownfish/Util/Memory.h" + +void* +Memory_wrapped_malloc(size_t count) { + void *pointer = malloc(count); + if (pointer == NULL && count != 0) { + fprintf(stderr, "Can't malloc %" U64P " bytes.\n", (uint64_t)count); + exit(1); + } + return pointer; +} + +void* +Memory_wrapped_calloc(size_t count, size_t size) { + void *pointer = calloc(count, size); + if (pointer == NULL && count != 0) { + fprintf(stderr, "Can't calloc %" U64P " elements of size %" U64P ".\n", + (uint64_t)count, (uint64_t)size); + exit(1); + } + return pointer; +} + +void* +Memory_wrapped_realloc(void *ptr, size_t size) { + void *pointer = realloc(ptr, size); + if (pointer == NULL && size != 0) { + fprintf(stderr, "Can't realloc %" U64P " bytes.\n", (uint64_t)size); + exit(1); + } + return pointer; +} + +void +Memory_wrapped_free(void *ptr) { + free(ptr); +} + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)-1) +#endif + +size_t +Memory_oversize(size_t minimum, size_t width) { + // For larger arrays, grow by an excess of 1/8; grow faster when the array + // is small. + size_t extra = minimum / 8; + if (extra < 3) { + extra = 3; + } + size_t amount = minimum + extra; + + // Detect wraparound and return SIZE_MAX instead. + if (amount + 7 < minimum) { + return SIZE_MAX; + } + + // Round up for small widths so that the number of bytes requested will be + // a multiple of the machine's word size. + if (sizeof(size_t) == 8) { // 64-bit + switch (width) { + case 1: + amount = (amount + 7) & CHY_I64_C(0xFFFFFFFFFFFFFFF8); + break; + case 2: + amount = (amount + 3) & CHY_I64_C(0xFFFFFFFFFFFFFFFC); + break; + case 4: + amount = (amount + 1) & CHY_I64_C(0xFFFFFFFFFFFFFFFE); + break; + default: + break; + } + } + else { // 32-bit + switch (width) { + case 1: + amount = (amount + 3) & ((size_t)0xFFFFFFFC); + break; + case 2: + amount = (amount + 1) & ((size_t)0xFFFFFFFE); + break; + default: + break; + } + } + + return amount; +} + + diff --git a/clownfish/core/Clownfish/Util/Memory.cfh b/clownfish/core/Clownfish/Util/Memory.cfh new file mode 100644 index 000000000..cd249b4cc --- /dev/null +++ b/clownfish/core/Clownfish/Util/Memory.cfh @@ -0,0 +1,73 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +inert class Clownfish::Util::Memory { + + /** Attempt to allocate memory with malloc, but print an error and exit if the + * call fails. + */ + inert nullable void* + wrapped_malloc(size_t count); + + /** Attempt to allocate memory with calloc, but print an error and exit if the + * call fails. + */ + inert nullable void* + wrapped_calloc(size_t count, size_t size); + + /** Attempt to allocate memory with realloc, but print an error and exit if + * the call fails. + */ + inert nullable void* + wrapped_realloc(void *ptr, size_t size); + + /** Free memory. (Wrapping is necessary in cases where memory allocated + * within the Lucy library has to be freed in an external environment where + * "free" may have been redefined.) + */ + inert void + wrapped_free(void *ptr); + + /** Provide a number which is somewhat larger than the supplied number, so + * that incremental array growth does not trigger pathological + * reallocation. + * + * @param minimum The minimum number of array elements. + * @param width The size of each array element in bytes. + */ + inert size_t + oversize(size_t minimum, size_t width); +} + +__C__ + +#define LUCY_MALLOCATE lucy_Memory_wrapped_malloc +#define LUCY_CALLOCATE lucy_Memory_wrapped_calloc +#define LUCY_REALLOCATE lucy_Memory_wrapped_realloc +#define LUCY_FREEMEM lucy_Memory_wrapped_free + +#ifdef LUCY_USE_SHORT_NAMES + #define MALLOCATE LUCY_MALLOCATE + #define CALLOCATE LUCY_CALLOCATE + #define REALLOCATE LUCY_REALLOCATE + #define FREEMEM LUCY_FREEMEM +#endif + +__END_C__ + + diff --git a/clownfish/core/Clownfish/Util/NumberUtils.c b/clownfish/core/Clownfish/Util/NumberUtils.c new file mode 100644 index 000000000..ba73cf968 --- /dev/null +++ b/clownfish/core/Clownfish/Util/NumberUtils.c @@ -0,0 +1,35 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_NUMBERUTILS +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include + +#include "Clownfish/Util/NumberUtils.h" + +const uint8_t NumUtil_u1masks[8] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 +}; + +const uint8_t NumUtil_u2shifts[4] = { 0x0, 0x2, 0x4, 0x6 }; +const uint8_t NumUtil_u2masks[4] = { 0x3, 0xC, 0x30, 0xC0 }; + +const uint8_t NumUtil_u4shifts[2] = { 0x00, 0x04 }; +const uint8_t NumUtil_u4masks[2] = { 0x0F, 0xF0 }; + + diff --git a/clownfish/core/Clownfish/Util/NumberUtils.cfh b/clownfish/core/Clownfish/Util/NumberUtils.cfh new file mode 100644 index 000000000..e820f5bab --- /dev/null +++ b/clownfish/core/Clownfish/Util/NumberUtils.cfh @@ -0,0 +1,474 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** Provide various number-related utilies. + * + * Provide utilities for dealing with endian issues, sub-byte-width arrays, + * compressed integers, and so on. + */ +inert class Clownfish::Util::NumberUtils cnick NumUtil { + + inert const uint8_t[8] u1masks; + inert const uint8_t[4] u2masks; + inert const uint8_t[4] u2shifts; + inert const uint8_t[2] u4masks; + inert const uint8_t[2] u4shifts; + + /** Encode an unsigned 16-bit integer as 2 bytes in the buffer provided, + * using big-endian byte order. + */ + inert inline void + encode_bigend_u16(uint16_t value, void *dest); + + /** Encode an unsigned 32-bit integer as 4 bytes in the buffer provided, + * using big-endian byte order. + */ + inert inline void + encode_bigend_u32(uint32_t value, void *dest); + + /** Encode an unsigned 64-bit integer as 8 bytes in the buffer provided, + * using big-endian byte order. + */ + inert inline void + encode_bigend_u64(uint64_t value, void *dest); + + /** Interpret a sequence of bytes as a big-endian unsigned 16-bit int. + */ + inert inline uint16_t + decode_bigend_u16(void *source); + + /** Interpret a sequence of bytes as a big-endian unsigned 32-bit int. + */ + inert inline uint32_t + decode_bigend_u32(void *source); + + /** Interpret a sequence of bytes as a big-endian unsigned 64-bit int. + */ + inert inline uint64_t + decode_bigend_u64(void *source); + + /** Encode a 32-bit floating point number as 4 bytes in the buffer + * provided, using big-endian byte order. + */ + inert inline void + encode_bigend_f32(float value, void *dest); + + /** Encode a 64-bit floating point number as 8 bytes in the buffer + * provided, using big-endian byte order. + */ + inert inline void + encode_bigend_f64(double value, void *dest); + + /** Interpret a sequence of bytes as a 32-bit float stored in big-endian + * byte order. + */ + inert inline float + decode_bigend_f32(void *source); + + /** Interpret a sequence of bytes as a 64-bit float stored in big-endian + * byte order. + */ + inert inline double + decode_bigend_f64(void *source); + + /** Encode a C32 at the space pointed to by dest. As a side + * effect, dest will be advanced to immediately after the end + * of the C32. + */ + inert inline void + encode_c32(uint32_t value, char **dest); + + /** Encode a C32 at the space pointed to by dest, but add + * "leading zeroes" so that the space consumed will always be 5 bytes. As + * a side effect, dest will be advanced to immediately after + * the end of the C32. + */ + inert inline void + encode_padded_c32(uint32_t value, char **dest); + + /** Encode a C64 at the space pointed to by dest. As a side + * effect, dest will be advanced to immediately after the end + * of the C64. + */ + inert inline void + encode_c64(uint64_t value, char **dest); + + /** Read a C32 from the buffer pointed to by source. As a + * side effect, advance the pointer, consuming the bytes occupied by the + * C32. + */ + inert inline uint32_t + decode_c32(char **source); + + /** Read a C64 from the buffer pointed to by source. As a + * side effect, advance the pointer, consuming the bytes occupied by the + * C64. + */ + inert inline uint64_t + decode_c64(char **source); + + /** Advance source past one encoded C32 or C64. + */ + inert inline void + skip_cint(char **source); + + /** Interpret array as an array of bits; return true if the + * bit at tick is set, false otherwise. + */ + inert inline bool_t + u1get(void *array, uint32_t tick); + + /** Interpret array as an array of bits; set the bit at + * tick. + */ + inert inline void + u1set(void *array, uint32_t tick); + + /** Interpret array as an array of bits; clear the bit at + * tick. + */ + inert inline void + u1clear(void *array, uint32_t tick); + + /** Interpret array as an array of bits; flip the bit at + * tick. + */ + inert inline void + u1flip(void *array, uint32_t tick); + + /** Interpret array as an array of two-bit integers; return + * the value at tick. + */ + inert inline uint8_t + u2get(void *array, uint32_t tick); + + /** Interpret array as an array of two-bit integers; set the + * element at tick to value. + */ + inert inline void + u2set(void *array, uint32_t tick, uint8_t value); + + /** Interpret array as an array of four-bit integers; return + * the value at tick. + */ + inert inline uint8_t + u4get(void *array, uint32_t tick); + + /** Interpret array as an array of four-bit integers; set the + * element at tick to value. + */ + inert inline void + u4set(void *array, uint32_t tick, uint8_t value); +} + +__C__ + +#include + +static CHY_INLINE void +lucy_NumUtil_encode_bigend_u16(uint16_t value, void *dest_ptr) { + uint8_t *dest = *(uint8_t**)dest_ptr; +#ifdef CHY_BIG_END + memcpy(dest, &value, sizeof(uint16_t)); +#else // little endian + uint8_t *source = (uint8_t*)&value; + dest[0] = source[1]; + dest[1] = source[0]; +#endif // CHY_BIG_END (and little endian) +} + +static CHY_INLINE void +lucy_NumUtil_encode_bigend_u32(uint32_t value, void *dest_ptr) { + uint8_t *dest = *(uint8_t**)dest_ptr; +#ifdef CHY_BIG_END + memcpy(dest, &value, sizeof(uint32_t)); +#else // little endian + uint8_t *source = (uint8_t*)&value; + dest[0] = source[3]; + dest[1] = source[2]; + dest[2] = source[1]; + dest[3] = source[0]; +#endif // CHY_BIG_END (and little endian) +} + +static CHY_INLINE void +lucy_NumUtil_encode_bigend_u64(uint64_t value, void *dest_ptr) { + uint8_t *dest = *(uint8_t**)dest_ptr; +#ifdef CHY_BIG_END + memcpy(dest, &value, sizeof(uint64_t)); +#else // little endian + uint8_t *source = (uint8_t*)&value; + dest[0] = source[7]; + dest[1] = source[6]; + dest[2] = source[5]; + dest[3] = source[4]; + dest[4] = source[3]; + dest[5] = source[2]; + dest[6] = source[1]; + dest[7] = source[0]; +#endif // CHY_BIG_END (and little endian) +} + +static CHY_INLINE uint16_t +lucy_NumUtil_decode_bigend_u16(void *source) { + uint8_t *const buf = (uint8_t*)source; + return (buf[0] << 8) | + (buf[1]); +} + +static CHY_INLINE uint32_t +lucy_NumUtil_decode_bigend_u32(void *source) { + uint8_t *const buf = (uint8_t*)source; + return (buf[0] << 24) | + (buf[1] << 16) | + (buf[2] << 8) | + (buf[3]); +} + +static CHY_INLINE uint64_t +lucy_NumUtil_decode_bigend_u64(void *source) { + uint8_t *const buf = (uint8_t*)source; + uint64_t high_bits = (buf[0] << 24) | + (buf[1] << 16) | + (buf[2] << 8) | + (buf[3]); + uint32_t low_bits = (buf[4] << 24) | + (buf[5] << 16) | + (buf[6] << 8) | + (buf[7]); + uint64_t retval = high_bits << 32; + retval |= low_bits; + return retval; +} + +static CHY_INLINE void +lucy_NumUtil_encode_bigend_f32(float value, void *dest_ptr) { + uint8_t *dest = *(uint8_t**)dest_ptr; +#ifdef CHY_BIG_END + memcpy(dest, &value, sizeof(float)); +#else + union { float f; uint32_t u32; } duo; + duo.f = value; + lucy_NumUtil_encode_bigend_u32(duo.u32, &dest); +#endif +} + +static CHY_INLINE void +lucy_NumUtil_encode_bigend_f64(double value, void *dest_ptr) { + uint8_t *dest = *(uint8_t**)dest_ptr; +#ifdef CHY_BIG_END + memcpy(dest, &value, sizeof(double)); +#else + union { double d; uint64_t u64; } duo; + duo.d = value; + lucy_NumUtil_encode_bigend_u64(duo.u64, &dest); +#endif +} + +static CHY_INLINE float +lucy_NumUtil_decode_bigend_f32(void *source) { + union { float f; uint32_t u32; } duo; + memcpy(&duo, source, sizeof(float)); +#ifdef CHY_LITTLE_END + duo.u32 = lucy_NumUtil_decode_bigend_u32(&duo.u32); +#endif + return duo.f; +} + +static CHY_INLINE double +lucy_NumUtil_decode_bigend_f64(void *source) { + union { double d; uint64_t u64; } duo; + memcpy(&duo, source, sizeof(double)); +#ifdef CHY_LITTLE_END + duo.u64 = lucy_NumUtil_decode_bigend_u64(&duo.u64); +#endif + return duo.d; +} + +#define LUCY_NUMUTIL_C32_MAX_BYTES ((sizeof(uint32_t) * 8 / 7) + 1) // 5 +#define LUCY_NUMUTIL_C64_MAX_BYTES ((sizeof(uint64_t) * 8 / 7) + 1) // 10 + +static CHY_INLINE void +lucy_NumUtil_encode_c32(uint32_t value, char **out_buf) { + uint8_t buf[LUCY_NUMUTIL_C32_MAX_BYTES]; + uint8_t *const limit = buf + sizeof(buf); + uint8_t *ptr = limit - 1; + int num_bytes; + // Write last byte first, which has no continue bit. + *ptr = value & 0x7f; + value >>= 7; + while (value) { + // Work backwards, writing bytes with continue bits set. + *--ptr = ((value & 0x7f) | 0x80); + value >>= 7; + } + num_bytes = limit - ptr; + memcpy(*out_buf, ptr, num_bytes); + *out_buf += num_bytes; +} + +static CHY_INLINE void +lucy_NumUtil_encode_c64(uint64_t value, char **out_buf) { + uint8_t buf[LUCY_NUMUTIL_C64_MAX_BYTES]; + uint8_t *const limit = buf + sizeof(buf); + uint8_t *ptr = limit - 1; + int num_bytes; + // Write last byte first, which has no continue bit. + *ptr = value & 0x7f; + value >>= 7; + while (value) { + // Work backwards, writing bytes with continue bits set. + *--ptr = ((value & 0x7f) | 0x80); + value >>= 7; + } + num_bytes = limit - ptr; + memcpy(*out_buf, ptr, num_bytes); + *out_buf += num_bytes; +} + +static CHY_INLINE void +lucy_NumUtil_encode_padded_c32(uint32_t value, char **out_buf) { + uint8_t buf[LUCY_NUMUTIL_C32_MAX_BYTES] + = { 0x80, 0x80, 0x80, 0x80, 0x80 }; + uint8_t *const limit = buf + sizeof(buf); + uint8_t *ptr = limit - 1; + // Write last byte first, which has no continue bit. + *ptr = value & 0x7f; + value >>= 7; + while (value) { + // Work backwards, writing bytes with continue bits set. + *--ptr = ((value & 0x7f) | 0x80); + value >>= 7; + } + memcpy(*out_buf, buf, LUCY_NUMUTIL_C32_MAX_BYTES); + *out_buf += sizeof(buf); +} + +// Decode a compressed integer up to size of 'var', advancing 'source' +#define LUCY_NUMUTIL_DECODE_CINT(var, source) \ + do { \ + var = (*source & 0x7f); \ + while (*source++ & 0x80) { \ + var = (*source & 0x7f) | (var << 7); \ + } \ + } while (0) + +static CHY_INLINE uint32_t +lucy_NumUtil_decode_c32(char **source_ptr) { + char *source = *source_ptr; + uint32_t decoded; + LUCY_NUMUTIL_DECODE_CINT(decoded, source); + *source_ptr = source; + return decoded; +} + +static CHY_INLINE uint64_t +lucy_NumUtil_decode_c64(char **source_ptr) { + char *source = *source_ptr; + uint64_t decoded; + LUCY_NUMUTIL_DECODE_CINT(decoded, source); + *source_ptr = source; + return decoded; +} + +static CHY_INLINE void +lucy_NumUtil_skip_cint(char **source_ptr) { + uint8_t *ptr = *(uint8_t**)source_ptr; + while ((*ptr++ & 0x80) != 0) { } + *source_ptr = (char*)ptr; +} + +static CHY_INLINE chy_bool_t +lucy_NumUtil_u1get(void *array, uint32_t tick) { + uint8_t *const u8bits = (uint8_t*)array; + const uint32_t byte_offset = tick >> 3; + const uint8_t mask = lucy_NumUtil_u1masks[tick & 0x7]; + return !((u8bits[byte_offset] & mask) == 0); +} + +static CHY_INLINE void +lucy_NumUtil_u1set(void *array, uint32_t tick) { + uint8_t *const u8bits = (uint8_t*)array; + const uint32_t byte_offset = tick >> 3; + const uint8_t mask = lucy_NumUtil_u1masks[tick & 0x7]; + u8bits[byte_offset] |= mask; +} + +static CHY_INLINE void +lucy_NumUtil_u1clear(void *array, uint32_t tick) { + uint8_t *const u8bits = (uint8_t*)array; + const uint32_t byte_offset = tick >> 3; + const uint8_t mask = lucy_NumUtil_u1masks[tick & 0x7]; + u8bits[byte_offset] &= ~mask; +} + +static CHY_INLINE void +lucy_NumUtil_u1flip(void *array, uint32_t tick) { + uint8_t *const u8bits = (uint8_t*)array; + const uint32_t byte_offset = tick >> 3; + const uint8_t mask = lucy_NumUtil_u1masks[tick & 0x7]; + u8bits[byte_offset] ^= mask; +} + +static CHY_INLINE uint8_t +lucy_NumUtil_u2get(void *array, uint32_t tick) { + uint8_t *ints = (uint8_t*)array; + uint8_t byte = ints[(tick >> 2)]; + int shift = lucy_NumUtil_u2shifts[tick & 0x3]; + return (byte >> shift) & 0x3; +} + +static CHY_INLINE void +lucy_NumUtil_u2set(void *array, uint32_t tick, uint8_t value) { + uint8_t *ints = (uint8_t*)array; + unsigned sub_tick = tick & 0x3; + int shift = lucy_NumUtil_u2shifts[sub_tick]; + uint8_t mask = lucy_NumUtil_u2masks[sub_tick]; + uint8_t new_val = value & 0x3; + uint8_t new_bits = new_val << shift; + ints[(tick >> 2)] = (ints[(tick >> 2)] & ~mask) | new_bits; +} + + +static CHY_INLINE uint8_t +lucy_NumUtil_u4get(void *array, uint32_t tick) { + uint8_t *ints = (uint8_t*)array; + uint8_t byte = ints[(tick >> 1)]; + int shift = lucy_NumUtil_u4shifts[(tick & 1)]; + return (byte >> shift) & 0xF; +} + +static CHY_INLINE void +lucy_NumUtil_u4set(void *array, uint32_t tick, uint8_t value) { + uint8_t *ints = (uint8_t*)array; + unsigned sub_tick = tick & 0x1; + int shift = lucy_NumUtil_u4shifts[sub_tick]; + uint8_t mask = lucy_NumUtil_u4masks[sub_tick]; + uint8_t new_val = value & 0xF; + uint8_t new_bits = new_val << shift; + ints[(tick >> 1)] = (ints[(tick >> 1)] & ~mask) | new_bits; +} + +#ifdef LUCY_USE_SHORT_NAMES + #define C32_MAX_BYTES LUCY_NUMUTIL_C32_MAX_BYTES + #define C64_MAX_BYTES LUCY_NUMUTIL_C64_MAX_BYTES +#endif + +__END_C__ + + diff --git a/clownfish/core/Clownfish/Util/SortUtils.c b/clownfish/core/Clownfish/Util/SortUtils.c new file mode 100644 index 000000000..71539cbef --- /dev/null +++ b/clownfish/core/Clownfish/Util/SortUtils.c @@ -0,0 +1,462 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_SORTUTILS +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include +#include "Clownfish/Util/SortUtils.h" +#include "Clownfish/Err.h" + +// Define four-byte and eight-byte types so that we can dereference void +// pointers like integer pointers. The only significance of using int32_t and +// int64_t is that they are 4 and 8 bytes. +#define FOUR_BYTE_TYPE int32_t +#define EIGHT_BYTE_TYPE int64_t + +/***************************** mergesort ************************************/ + +// Recursive merge sorting functions. +static void +S_msort4(void *velems, void *vscratch, uint32_t left, uint32_t right, + Lucy_Sort_Compare_t compare, void *context); +static void +S_msort8(void *velems, void *vscratch, uint32_t left, uint32_t right, + Lucy_Sort_Compare_t compare, void *context); +static void +S_msort_any(void *velems, void *vscratch, uint32_t left, uint32_t right, + Lucy_Sort_Compare_t compare, void *context, size_t width); + +static INLINE void +SI_merge(void *left_vptr, uint32_t left_size, + void *right_vptr, uint32_t right_size, + void *vdest, size_t width, Lucy_Sort_Compare_t compare, void *context); + +void +Sort_mergesort(void *elems, void *scratch, uint32_t num_elems, uint32_t width, + Lucy_Sort_Compare_t compare, void *context) { + // Arrays of 0 or 1 items are already sorted. + if (num_elems < 2) { return; } + + // Validate. + if (num_elems >= I32_MAX) { + THROW(ERR, "Provided %u64 elems, but can't handle more than %i32", + (uint64_t)num_elems, I32_MAX); + } + + // Dispatch by element size. + switch (width) { + case 0: + THROW(ERR, "Parameter 'width' cannot be 0"); + break; + case 4: + S_msort4(elems, scratch, 0, num_elems - 1, compare, context); + break; + case 8: + S_msort8(elems, scratch, 0, num_elems - 1, compare, context); + break; + default: + S_msort_any(elems, scratch, 0, num_elems - 1, compare, + context, width); + break; + } +} + +void +Sort_merge(void *left_ptr, uint32_t left_size, + void *right_ptr, uint32_t right_size, + void *dest, size_t width, Lucy_Sort_Compare_t compare, + void *context) { + switch (width) { + case 0: + THROW(ERR, "Parameter 'width' cannot be 0"); + break; + case 4: + SI_merge(left_ptr, left_size, right_ptr, right_size, + dest, 4, compare, context); + break; + case 8: + SI_merge(left_ptr, left_size, right_ptr, right_size, + dest, 8, compare, context); + break; + default: + SI_merge(left_ptr, left_size, right_ptr, right_size, + dest, width, compare, context); + break; + } +} + +#define WIDTH 4 +static void +S_msort4(void *velems, void *vscratch, uint32_t left, uint32_t right, + Lucy_Sort_Compare_t compare, void *context) { + uint8_t *elems = (uint8_t*)velems; + uint8_t *scratch = (uint8_t*)vscratch; + if (right > left) { + const uint32_t mid = ((right + left) / 2) + 1; + S_msort4(elems, scratch, left, mid - 1, compare, context); + S_msort4(elems, scratch, mid, right, compare, context); + SI_merge((elems + left * WIDTH), (mid - left), + (elems + mid * WIDTH), (right - mid + 1), + scratch, WIDTH, compare, context); + memcpy((elems + left * WIDTH), scratch, ((right - left + 1) * WIDTH)); + } +} + +#undef WIDTH +#define WIDTH 8 +static void +S_msort8(void *velems, void *vscratch, uint32_t left, uint32_t right, + Lucy_Sort_Compare_t compare, void *context) { + uint8_t *elems = (uint8_t*)velems; + uint8_t *scratch = (uint8_t*)vscratch; + if (right > left) { + const uint32_t mid = ((right + left) / 2) + 1; + S_msort8(elems, scratch, left, mid - 1, compare, context); + S_msort8(elems, scratch, mid, right, compare, context); + SI_merge((elems + left * WIDTH), (mid - left), + (elems + mid * WIDTH), (right - mid + 1), + scratch, WIDTH, compare, context); + memcpy((elems + left * WIDTH), scratch, ((right - left + 1) * WIDTH)); + } +} + +#undef WIDTH +static void +S_msort_any(void *velems, void *vscratch, uint32_t left, uint32_t right, + Lucy_Sort_Compare_t compare, void *context, size_t width) { + uint8_t *elems = (uint8_t*)velems; + uint8_t *scratch = (uint8_t*)vscratch; + if (right > left) { + const uint32_t mid = ((right + left) / 2) + 1; + S_msort_any(elems, scratch, left, mid - 1, compare, context, width); + S_msort_any(elems, scratch, mid, right, compare, context, width); + SI_merge((elems + left * width), (mid - left), + (elems + mid * width), (right - mid + 1), + scratch, width, compare, context); + memcpy((elems + left * width), scratch, ((right - left + 1) * width)); + } +} + +static INLINE void +SI_merge(void *left_vptr, uint32_t left_size, + void *right_vptr, uint32_t right_size, + void *vdest, size_t width, Lucy_Sort_Compare_t compare, + void *context) { + uint8_t *left_ptr = (uint8_t*)left_vptr; + uint8_t *right_ptr = (uint8_t*)right_vptr; + uint8_t *left_limit = left_ptr + left_size * width; + uint8_t *right_limit = right_ptr + right_size * width; + uint8_t *dest = (uint8_t*)vdest; + + while (left_ptr < left_limit && right_ptr < right_limit) { + if (compare(context, left_ptr, right_ptr) < 1) { + memcpy(dest, left_ptr, width); + dest += width; + left_ptr += width; + } + else { + memcpy(dest, right_ptr, width); + dest += width; + right_ptr += width; + } + } + + const size_t left_remaining = left_limit - left_ptr; + memcpy(dest, left_ptr, left_remaining); + dest += left_remaining; + const size_t right_remaining = right_limit - right_ptr; + memcpy(dest, right_ptr, right_remaining); +} + +/***************************** quicksort ************************************/ + +// Quicksort implementations optimized for four-byte and eight-byte elements. +static void +S_qsort4(FOUR_BYTE_TYPE *elems, int32_t left, int32_t right, + Lucy_Sort_Compare_t compare, void *context); +static void +S_qsort8(EIGHT_BYTE_TYPE *elems, int32_t left, int32_t right, + Lucy_Sort_Compare_t compare, void *context); + +// Swap two elements. +static INLINE void +SI_exchange4(FOUR_BYTE_TYPE *elems, int32_t left, int32_t right); +static INLINE void +SI_exchange8(EIGHT_BYTE_TYPE *elems, int32_t left, int32_t right); + +/* Select a pivot by choosing the median of three values, guarding against + * the worst-case behavior of quicksort. Place the pivot in the rightmost + * slot. + * + * Possible states: + * + * abc => abc => abc => acb + * acb => acb => acb => acb + * bac => abc => abc => acb + * bca => bca => acb => acb + * cba => bca => acb => acb + * cab => acb => acb => acb + * aab => aab => aab => aba + * aba => aba => aba => aba + * baa => aba => aba => aba + * bba => bba => abb => abb + * bab => abb => abb => abb + * abb => abb => abb => abb + * aaa => aaa => aaa => aaa + */ +static INLINE FOUR_BYTE_TYPE* +SI_choose_pivot4(FOUR_BYTE_TYPE *elems, int32_t left, int32_t right, + Lucy_Sort_Compare_t compare, void *context); +static INLINE EIGHT_BYTE_TYPE* +SI_choose_pivot8(EIGHT_BYTE_TYPE *elems, int32_t left, int32_t right, + Lucy_Sort_Compare_t compare, void *context); + +void +Sort_quicksort(void *elems, size_t num_elems, size_t width, + Lucy_Sort_Compare_t compare, void *context) { + // Arrays of 0 or 1 items are already sorted. + if (num_elems < 2) { return; } + + // Validate. + if (num_elems >= I32_MAX) { + THROW(ERR, "Provided %u64 elems, but can't handle more than %i32", + (uint64_t)num_elems, I32_MAX); + } + + if (width == 4) { + S_qsort4((FOUR_BYTE_TYPE*)elems, 0, num_elems - 1, compare, context); + } + else if (width == 8) { + S_qsort8((EIGHT_BYTE_TYPE*)elems, 0, num_elems - 1, compare, context); + } + else { + THROW(ERR, "Unsupported width: %i64", (int64_t)width); + } +} + +/************************* quicksort 4 byte *********************************/ + +static INLINE void +SI_exchange4(FOUR_BYTE_TYPE *elems, int32_t left, int32_t right) { + FOUR_BYTE_TYPE saved = elems[left]; + elems[left] = elems[right]; + elems[right] = saved; +} + +static INLINE FOUR_BYTE_TYPE* +SI_choose_pivot4(FOUR_BYTE_TYPE *elems, int32_t left, int32_t right, + Lucy_Sort_Compare_t compare, void *context) { + if (right - left > 1) { + int32_t mid = left + (right - left) / 2; + if (compare(context, elems + left, elems + mid) > 0) { + SI_exchange4(elems, left, mid); + } + if (compare(context, elems + left, elems + right) > 0) { + SI_exchange4(elems, left, right); + } + if (compare(context, elems + right, elems + mid) > 0) { + SI_exchange4(elems, right, mid); + } + } + return elems + right; +} + +static void +S_qsort4(FOUR_BYTE_TYPE *elems, int32_t left, int32_t right, + Lucy_Sort_Compare_t compare, void *context) { + FOUR_BYTE_TYPE *const pivot + = SI_choose_pivot4(elems, left, right, compare, context); + int32_t i = left - 1; + int32_t j = right; + int32_t p = left - 1; + int32_t q = right; + + if (right <= left) { return; } + + /* TODO: A standard optimization for quicksort is to fall back to an + * insertion sort when the the number of elements to be sorted becomes + * small enough. */ + + while (1) { + int comparison1; + int comparison2; + + // Find an element from the left that is greater than or equal to the + // pivot (i.e. that should move to the right). + while (1) { + i++; + comparison1 = compare(context, elems + i, pivot); + if (comparison1 >= 0) { break; } + if (i == right) { break; } + } + + // Find an element from the right that is less than or equal to the + // pivot (i.e. that should move to the left). + while (1) { + j--; + comparison2 = compare(context, elems + j, pivot); + if (comparison2 <= 0) { break; } + if (j == left) { break; } + } + + // Bail out of loop when we meet in the middle. + if (i >= j) { break; } + + // Swap the elements we found, so the lesser element moves left and + // the greater element moves right. + SI_exchange4(elems, i, j); + + // Move any elements which test as "equal" to the pivot to the outside + // edges of the array. + if (comparison2 == 0) { + p++; + SI_exchange4(elems, p, i); + } + if (comparison1 == 0) { + q--; + SI_exchange4(elems, j, q); + } + } + + /* Move "equal" elements from the outside edges to the center. + * + * Before: + * + * equal | less_than | greater_than | equal + * + * After: + * + * less_than | equal | greater_than + */ + SI_exchange4(elems, i, right); + j = i - 1; + i++; + for (int32_t k = left; k < p; k++, j--) { SI_exchange4(elems, k, j); } + for (int32_t k = right - 1; k > q; k--, i++) { SI_exchange4(elems, i, k); } + + // Recurse. + S_qsort4(elems, left, j, compare, context); // Sort less_than. + S_qsort4(elems, i, right, compare, context); // Sort greater_than. +} + +/************************* quicksort 8 byte *********************************/ + +static INLINE void +SI_exchange8(EIGHT_BYTE_TYPE *elems, int32_t left, int32_t right) { + EIGHT_BYTE_TYPE saved = elems[left]; + elems[left] = elems[right]; + elems[right] = saved; +} + +static INLINE EIGHT_BYTE_TYPE* +SI_choose_pivot8(EIGHT_BYTE_TYPE *elems, int32_t left, int32_t right, + Lucy_Sort_Compare_t compare, void *context) { + if (right - left > 1) { + int32_t mid = left + (right - left) / 2; + if (compare(context, elems + left, elems + mid) > 0) { + SI_exchange8(elems, left, mid); + } + if (compare(context, elems + left, elems + right) > 0) { + SI_exchange8(elems, left, right); + } + if (compare(context, elems + right, elems + mid) > 0) { + SI_exchange8(elems, right, mid); + } + } + return elems + right; +} + +static void +S_qsort8(EIGHT_BYTE_TYPE *elems, int32_t left, int32_t right, + Lucy_Sort_Compare_t compare, void *context) { + EIGHT_BYTE_TYPE *const pivot + = SI_choose_pivot8(elems, left, right, compare, context); + int32_t i = left - 1; + int32_t j = right; + int32_t p = left - 1; + int32_t q = right; + + if (right <= left) { return; } + + /* TODO: A standard optimization for quicksort is to fall back to an + * insertion sort when the the number of elements to be sorted becomes + * small enough. */ + + while (1) { + int comparison1; + int comparison2; + + // Find an element from the left that is greater than or equal to the + // pivot (i.e. that should move to the right). + while (1) { + i++; + comparison1 = compare(context, elems + i, pivot); + if (comparison1 >= 0) { break; } + if (i == right) { break; } + } + + // Find an element from the right that is less than or equal to the + // pivot (i.e. that should move to the left). + while (1) { + j--; + comparison2 = compare(context, elems + j, pivot); + if (comparison2 <= 0) { break; } + if (j == left) { break; } + } + + // Bail out of loop when we meet in the middle. + if (i >= j) { break; } + + // Swap the elements we found, so the lesser element moves left and + // the greater element moves right. + SI_exchange8(elems, i, j); + + // Move any elements which test as "equal" to the pivot to the outside + // edges of the array. + if (comparison2 == 0) { + p++; + SI_exchange8(elems, p, i); + } + if (comparison1 == 0) { + q--; + SI_exchange8(elems, j, q); + } + } + + /* Move "equal" elements from the outside edges to the center. + * + * Before: + * + * equal | less_than | greater_than | equal + * + * After: + * + * less_than | equal | greater_than + */ + SI_exchange8(elems, i, right); + j = i - 1; + i++; + for (int32_t k = left; k < p; k++, j--) { SI_exchange8(elems, k, j); } + for (int32_t k = right - 1; k > q; k--, i++) { SI_exchange8(elems, i, k); } + + // Recurse. + S_qsort8(elems, left, j, compare, context); // Sort less_than. + S_qsort8(elems, i, right, compare, context); // Sort greater_than. +} + + diff --git a/clownfish/core/Clownfish/Util/SortUtils.cfh b/clownfish/core/Clownfish/Util/SortUtils.cfh new file mode 100644 index 000000000..4071a32b5 --- /dev/null +++ b/clownfish/core/Clownfish/Util/SortUtils.cfh @@ -0,0 +1,68 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +__C__ +typedef int +(*Lucy_Sort_Compare_t)(void *context, const void *va, const void *vb); +__END_C__ + +/** Specialized sorting routines. + * + * SortUtils provides a merge sort algorithm which allows access to its + * internals, enabling specialized functions to jump in and only execute part + * of the sort. + * + * SortUtils also provides a quicksort with an additional context argument. + */ +inert class Clownfish::Util::SortUtils cnick Sort { + + /** Perform a mergesort. In addition to providing a contiguous array of + * elements to be sorted and their count, the caller must also provide a + * scratch buffer with room for at least as many elements as are to be + * sorted. + */ + inert void + mergesort(void *elems, void *scratch, uint32_t num_elems, uint32_t width, + Lucy_Sort_Compare_t compare, void *context); + + /** Merge two source arrays together using the classic mergesort merge + * algorithm, storing the result in dest. + * + * Most merge functions operate on a single contiguous array and copy the + * merged results results back into the source array before returning. + * These two differ in that it is possible to operate on two discontiguous + * source arrays. Copying the results back into the source array is the + * responsibility of the caller. + * + * Lucy's external sort takes advantage of this when it is reading + * back pre-sorted runs from disk and merging the streams into a + * consolidated buffer. + */ + inert void + merge(void *left_ptr, uint32_t left_num_elems, + void *right_ptr, uint32_t right_num_elems, + void *dest, size_t width, Lucy_Sort_Compare_t compare, void *context); + + /** Quicksort. + */ + inert void + quicksort(void *elems, size_t num_elems, size_t width, + Lucy_Sort_Compare_t compare, void *context); +} + + diff --git a/clownfish/core/Clownfish/Util/StringHelper.c b/clownfish/core/Clownfish/Util/StringHelper.c new file mode 100644 index 000000000..9f356bf3b --- /dev/null +++ b/clownfish/core/Clownfish/Util/StringHelper.c @@ -0,0 +1,229 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_STRINGHELPER +#include + +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include "Clownfish/Util/StringHelper.h" +#include "Clownfish/Err.h" +#include "Clownfish/Util/Memory.h" + +const uint8_t lucy_StrHelp_UTF8_COUNT[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +int32_t +StrHelp_overlap(const char *a, const char *b, size_t a_len, size_t b_len) { + size_t i; + const size_t len = a_len <= b_len ? a_len : b_len; + + for (i = 0; i < len; i++) { + if (*a++ != *b++) { break; } + } + return i; +} + +static const char base36_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +uint32_t +StrHelp_to_base36(uint64_t num, void *buffer) { + char my_buf[StrHelp_MAX_BASE36_BYTES]; + char *buf = my_buf + StrHelp_MAX_BASE36_BYTES - 1; + char *end = buf; + + // Null terminate. + *buf = '\0'; + + // Convert to base 36 characters. + do { + *(--buf) = base36_chars[num % 36]; + num /= 36; + } while (num > 0); + + uint32_t size = end - buf; + memcpy(buffer, buf, size + 1); + return size; +} + +bool_t +StrHelp_utf8_valid(const char *ptr, size_t size) { + const uint8_t *string = (const uint8_t*)ptr; + const uint8_t *const end = string + size; + while (string < end) { + const uint8_t header_byte = *string++; + int count = StrHelp_UTF8_COUNT[header_byte] & 0x7; + switch (count & 0x7) { + case 1: + // ASCII + break; + case 2: + if (string == end) { return false; } + // Disallow non-shortest-form ASCII. + if (!(header_byte & 0x1E)) { return false; } + if ((*string++ & 0xC0) != 0x80) { return false; } + break; + case 3: + if (end - string < 2) { return false; } + if (header_byte == 0xED) { + if (*string < 0x80 || *string > 0x9F) { + return false; + } + } + else if (!(header_byte & 0x0F)) { + if (!(*string & 0x20)) { + return false; + } + } + if ((*string++ & 0xC0) != 0x80) { return false; } + if ((*string++ & 0xC0) != 0x80) { return false; } + break; + case 4: + if (end - string < 3) { return false; } + if (!(header_byte & 0x07)) { + if (!(*string & 0x30)) { + return false; + } + } + if ((*string++ & 0xC0) != 0x80) { return false; } + if ((*string++ & 0xC0) != 0x80) { return false; } + if ((*string++ & 0xC0) != 0x80) { return false; } + break; + default: + return false; + } + } + + return true; +} + +bool_t +StrHelp_is_whitespace(uint32_t code_point) { + switch (code_point) { + // .. + case 0x0009: case 0x000A: case 0x000B: case 0x000C: case 0x000D: + case 0x0020: // SPACE + case 0x0085: // + case 0x00A0: // NO-BREAK SPACE + case 0x1680: // OGHAM SPACE MARK + case 0x180E: // MONGOLIAN VOWEL SEPARATOR + // EN QUAD..HAIR SPACE + case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: + case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: + case 0x200A: + case 0x2028: // LINE SEPARATOR + case 0x2029: // PARAGRAPH SEPARATOR + case 0x202F: // NARROW NO-BREAK SPACE + case 0x205F: // MEDIUM MATHEMATICAL SPACE + case 0x3000: // IDEOGRAPHIC SPACE + return true; + + default: + return false; + } +} + +uint32_t +StrHelp_encode_utf8_char(uint32_t code_point, void *buffer) { + uint8_t *buf = (uint8_t*)buffer; + if (code_point <= 0x7F) { // ASCII + buf[0] = (uint8_t)code_point; + return 1; + } + else if (code_point <= 0x07FF) { // 2 byte range + buf[0] = (uint8_t)(0xC0 | (code_point >> 6)); + buf[1] = (uint8_t)(0x80 | (code_point & 0x3f)); + return 2; + } + else if (code_point <= 0xFFFF) { // 3 byte range + buf[0] = (uint8_t)(0xE0 | (code_point >> 12)); + buf[1] = (uint8_t)(0x80 | ((code_point >> 6) & 0x3F)); + buf[2] = (uint8_t)(0x80 | (code_point & 0x3f)); + return 3; + } + else if (code_point <= 0x10FFFF) { // 4 byte range + buf[0] = (uint8_t)(0xF0 | (code_point >> 18)); + buf[1] = (uint8_t)(0x80 | ((code_point >> 12) & 0x3F)); + buf[2] = (uint8_t)(0x80 | ((code_point >> 6) & 0x3F)); + buf[3] = (uint8_t)(0x80 | (code_point & 0x3f)); + return 4; + } + else { + THROW(ERR, "Illegal Unicode code point: %u32", code_point); + UNREACHABLE_RETURN(uint32_t); + } +} + +uint32_t +StrHelp_decode_utf8_char(const char *ptr) { + const uint8_t *const string = (const uint8_t*)ptr; + uint32_t retval = *string; + int bytes = StrHelp_UTF8_COUNT[retval]; + + switch (bytes & 0x7) { + case 1: + break; + + case 2: + retval = ((retval & 0x1F) << 6) + | (string[1] & 0x3F); + break; + + case 3: + retval = ((retval & 0x0F) << 12) + | ((string[1] & 0x3F) << 6) + | (string[2] & 0x3F); + break; + + case 4: + retval = ((retval & 0x07) << 18) + | ((string[1] & 0x3F) << 12) + | ((string[2] & 0x3F) << 6) + | (string[3] & 0x3F); + break; + + default: + THROW(ERR, "Invalid UTF-8 header byte: %x32", retval); + } + + return retval; +} + +const char* +StrHelp_back_utf8_char(const char *ptr, char *start) { + while (--ptr >= start) { + if ((*ptr & 0xC0) != 0x80) { return ptr; } + } + return NULL; +} + diff --git a/clownfish/core/Clownfish/Util/StringHelper.cfh b/clownfish/core/Clownfish/Util/StringHelper.cfh new file mode 100644 index 000000000..cd97f3c50 --- /dev/null +++ b/clownfish/core/Clownfish/Util/StringHelper.cfh @@ -0,0 +1,83 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +inert class Clownfish::Util::StringHelper cnick StrHelp { + + /* A table where the values indicate the number of bytes in a UTF-8 + * sequence implied by the leading utf8 byte. + */ + inert const uint8_t[] UTF8_COUNT; + + /** Return the number of bytes that two strings have in common. + */ + inert int32_t + overlap(const char *a, const char *b, size_t a_len, size_t b_len); + + /** Encode a NULL-terminated string representation of a value in base 36 + * into buffer. + * + * @param value The number to be encoded. + * @param buffer A buffer at least MAX_BASE36_BYTES bytes long. + * @return the number of digits encoded (not including the terminating + * NULL). + */ + inert uint32_t + to_base36(uint64_t value, void *buffer); + + /** Return true if the string is valid UTF-8, false otherwise. + */ + inert bool_t + utf8_valid(const char *ptr, size_t len); + + /** Returns true if the code point qualifies as Unicode whitespace. + */ + inert bool_t + is_whitespace(uint32_t code_point); + + /** Encode a Unicode code point to a UTF-8 sequence. + * + * @param code_point A legal unicode code point. + * @param buffer Write buffer which must hold at least 4 bytes (the + * maximum legal length for a UTF-8 char). + */ + inert uint32_t + encode_utf8_char(uint32_t code_point, void *buffer); + + /** Decode a UTF-8 sequence to a Unicode code point. Assumes valid UTF-8. + */ + inert uint32_t + decode_utf8_char(const char *utf8); + + /** Return the first non-continuation byte before the supplied pointer. + * If backtracking progresses beyond the supplied start, return NULL. + */ + inert nullable const char* + back_utf8_char(const char *utf8, char *start); +} + +__C__ +/** The maximum number of bytes encoded by to_base36(), including the + * terminating NULL. + */ +#define lucy_StrHelp_MAX_BASE36_BYTES 14 +#ifdef LUCY_USE_SHORT_NAMES + #define StrHelp_MAX_BASE36_BYTES lucy_StrHelp_MAX_BASE36_BYTES +#endif +__END_C__ + + diff --git a/clownfish/core/Clownfish/VArray.c b/clownfish/core/Clownfish/VArray.c new file mode 100644 index 000000000..cb137791f --- /dev/null +++ b/clownfish/core/Clownfish/VArray.c @@ -0,0 +1,370 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_VARRAY +#include +#include + +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include "Clownfish/VTable.h" +#include "Clownfish/VArray.h" +#include "Clownfish/Err.h" +#include "Clownfish/Util/Memory.h" +#include "Lucy/Util/Freezer.h" +#include "Clownfish/Util/SortUtils.h" +#include "Lucy/Store/InStream.h" +#include "Lucy/Store/OutStream.h" + +VArray* +VA_new(uint32_t capacity) { + VArray *self = (VArray*)VTable_Make_Obj(VARRAY); + VA_init(self, capacity); + return self; +} + +VArray* +VA_init(VArray *self, uint32_t capacity) { + // Init. + self->size = 0; + + // Assign. + self->cap = capacity; + + // Derive. + self->elems = (Obj**)CALLOCATE(capacity, sizeof(Obj*)); + + return self; +} + +void +VA_destroy(VArray *self) { + if (self->elems) { + Obj **elems = self->elems; + Obj **const limit = elems + self->size; + for (; elems < limit; elems++) { + DECREF(*elems); + } + FREEMEM(self->elems); + } + SUPER_DESTROY(self, VARRAY); +} + +VArray* +VA_dump(VArray *self) { + VArray *dump = VA_new(self->size); + for (uint32_t i = 0, max = self->size; i < max; i++) { + Obj *elem = VA_Fetch(self, i); + if (elem) { VA_Store(dump, i, Obj_Dump(elem)); } + } + return dump; +} + +VArray* +VA_load(VArray *self, Obj *dump) { + VArray *source = (VArray*)CERTIFY(dump, VARRAY); + VArray *loaded = VA_new(source->size); + UNUSED_VAR(self); + + for (uint32_t i = 0, max = source->size; i < max; i++) { + Obj *elem_dump = VA_Fetch(source, i); + if (elem_dump) { + VA_Store(loaded, i, Obj_Load(elem_dump, elem_dump)); + } + } + + return loaded; +} + +void +VA_serialize(VArray *self, OutStream *outstream) { + uint32_t last_valid_tick = 0; + OutStream_Write_C32(outstream, self->size); + for (uint32_t i = 0; i < self->size; i++) { + Obj *elem = self->elems[i]; + if (elem) { + OutStream_Write_C32(outstream, i - last_valid_tick); + FREEZE(elem, outstream); + last_valid_tick = i; + } + } + // Terminate. + OutStream_Write_C32(outstream, self->size - last_valid_tick); +} + +VArray* +VA_deserialize(VArray *self, InStream *instream) { + uint32_t size = InStream_Read_C32(instream); + VA_Grow(self, size); + for (uint32_t tick = InStream_Read_C32(instream); + tick < size; + tick += InStream_Read_C32(instream) + ) { + Obj *obj = THAW(instream); + self->elems[tick] = obj; + } + self->size = size; + return self; +} + +VArray* +VA_clone(VArray *self) { + VArray *twin = VA_new(self->size); + + // Clone each element. + for (uint32_t i = 0; i < self->size; i++) { + Obj *elem = self->elems[i]; + if (elem) { + twin->elems[i] = Obj_Clone(elem); + } + } + + // Ensure that size is the same if NULL elems at end. + twin->size = self->size; + + return twin; +} + +VArray* +VA_shallow_copy(VArray *self) { + // Dupe, then increment refcounts. + VArray *twin = VA_new(self->size); + Obj **elems = twin->elems; + memcpy(elems, self->elems, self->size * sizeof(Obj*)); + twin->size = self->size; + for (uint32_t i = 0; i < self->size; i++) { + if (elems[i] != NULL) { + (void)INCREF(elems[i]); + } + } + + return twin; +} + +void +VA_push(VArray *self, Obj *element) { + if (self->size == self->cap) { + VA_Grow(self, Memory_oversize(self->size + 1, sizeof(Obj*))); + } + self->elems[self->size] = element; + self->size++; +} + +void +VA_push_varray(VArray *self, VArray *other) { + uint32_t tick = self->size; + uint32_t new_size = self->size + other->size; + if (new_size > self->cap) { + VA_Grow(self, Memory_oversize(new_size, sizeof(Obj*))); + } + for (uint32_t i = 0; i < other->size; i++, tick++) { + Obj *elem = VA_Fetch(other, i); + if (elem != NULL) { + self->elems[tick] = INCREF(elem); + } + } + self->size = new_size; +} + +Obj* +VA_pop(VArray *self) { + if (!self->size) { + return NULL; + } + self->size--; + return self->elems[self->size]; +} + +void +VA_unshift(VArray *self, Obj *elem) { + if (self->size == self->cap) { + VA_Grow(self, Memory_oversize(self->size + 1, sizeof(Obj*))); + } + memmove(self->elems + 1, self->elems, self->size * sizeof(Obj*)); + self->elems[0] = elem; + self->size++; +} + +Obj* +VA_shift(VArray *self) { + if (!self->size) { + return NULL; + } + else { + Obj *const return_val = self->elems[0]; + self->size--; + if (self->size > 0) { + memmove(self->elems, self->elems + 1, + self->size * sizeof(Obj*)); + } + return return_val; + } +} + +Obj* +VA_fetch(VArray *self, uint32_t num) { + if (num >= self->size) { + return NULL; + } + + return self->elems[num]; +} + +void +VA_store(VArray *self, uint32_t tick, Obj *elem) { + if (tick >= self->cap) { + VA_Grow(self, Memory_oversize(tick + 1, sizeof(Obj*))); + } + if (tick < self->size) { DECREF(self->elems[tick]); } + else { self->size = tick + 1; } + self->elems[tick] = elem; +} + +void +VA_grow(VArray *self, uint32_t capacity) { + if (capacity > self->cap) { + self->elems = (Obj**)REALLOCATE(self->elems, capacity * sizeof(Obj*)); + self->cap = capacity; + memset(self->elems + self->size, 0, + (capacity - self->size) * sizeof(Obj*)); + } +} + +Obj* +VA_delete(VArray *self, uint32_t num) { + Obj *elem = NULL; + if (num < self->size) { + elem = self->elems[num]; + self->elems[num] = NULL; + } + return elem; +} + +void +VA_excise(VArray *self, uint32_t offset, uint32_t length) { + if (self->size <= offset) { return; } + else if (self->size < offset + length) { length = self->size - offset; } + + for (uint32_t i = 0; i < length; i++) { + DECREF(self->elems[offset + i]); + } + + uint32_t num_to_move = self->size - (offset + length); + memmove(self->elems + offset, self->elems + offset + length, + num_to_move * sizeof(Obj*)); + self->size -= length; +} + +void +VA_clear(VArray *self) { + VA_excise(self, 0, self->size); +} + +void +VA_resize(VArray *self, uint32_t size) { + if (size < self->size) { + VA_Excise(self, size, self->size - size); + } + else if (size > self->size) { + VA_Grow(self, size); + } + self->size = size; +} + +uint32_t +VA_get_size(VArray *self) { + return self->size; +} + +uint32_t +VA_get_capacity(VArray *self) { + return self->cap; +} + +static int +S_default_compare(void *context, const void *va, const void *vb) { + Obj *a = *(Obj**)va; + Obj *b = *(Obj**)vb; + UNUSED_VAR(context); + if (a != NULL && b != NULL) { return Obj_Compare_To(a, b); } + else if (a == NULL && b == NULL) { return 0; } + else if (a == NULL) { return 1; } // NULL to the back + else /* b == NULL */ { return -1; } // NULL to the back +} + +void +VA_sort(VArray *self, Lucy_Sort_Compare_t compare, void *context) { + if (!compare) { compare = S_default_compare; } + Sort_quicksort(self->elems, self->size, sizeof(void*), compare, context); +} + +bool_t +VA_equals(VArray *self, Obj *other) { + VArray *twin = (VArray*)other; + if (twin == self) { return true; } + if (!Obj_Is_A(other, VARRAY)) { return false; } + if (twin->size != self->size) { + return false; + } + else { + for (uint32_t i = 0, max = self->size; i < max; i++) { + Obj *val = self->elems[i]; + Obj *other_val = twin->elems[i]; + if ((val && !other_val) || (other_val && !val)) { return false; } + if (val && !Obj_Equals(val, other_val)) { return false; } + } + } + return true; +} + +VArray* +VA_gather(VArray *self, Lucy_VA_Gather_Test_t test, void *data) { + VArray *gathered = VA_new(self->size); + for (uint32_t i = 0, max = self->size; i < max; i++) { + if (test(self, i, data)) { + Obj *elem = self->elems[i]; + VA_Push(gathered, elem ? INCREF(elem) : NULL); + } + } + return gathered; +} + +VArray* +VA_slice(VArray *self, uint32_t offset, uint32_t length) { + // Adjust ranges if necessary. + if (offset >= self->size) { + offset = 0; + length = 0; + } + else if (length > UINT32_MAX - offset + || offset + length > self->size + ) { + length = self->size - offset; + } + + // Copy elements. + VArray *slice = VA_new(length); + slice->size = length; + Obj **slice_elems = slice->elems; + Obj **my_elems = self->elems; + for (uint32_t i = 0; i < length; i++) { + slice_elems[i] = INCREF(my_elems[offset + i]); + } + + return slice; +} + diff --git a/clownfish/core/Clownfish/VArray.cfh b/clownfish/core/Clownfish/VArray.cfh new file mode 100644 index 000000000..dcc045517 --- /dev/null +++ b/clownfish/core/Clownfish/VArray.cfh @@ -0,0 +1,181 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +__C__ +#include "Clownfish/Util/SortUtils.h" + +typedef chy_bool_t +(*Lucy_VA_Gather_Test_t)(lucy_VArray *self, uint32_t tick, void *data); + +#ifdef LUCY_USE_SHORT_NAMES + #define Lucy_VA_Gather_Test_t Lucy_VA_Gather_Test_t +#endif +__END_C__ + +/** Variable-sized array. + */ +class Clownfish::VArray cnick VA inherits Clownfish::Obj { + + Obj **elems; + uint32_t size; + uint32_t cap; + + inert incremented VArray* + new(uint32_t capacity); + + /** + * @param capacity Initial number of elements that the object will be able + * to hold before reallocation. + */ + inert VArray* + init(VArray *self, uint32_t capacity); + + /** Push an item onto the end of a VArray. + */ + void + Push(VArray *self, decremented Obj *element = NULL); + + /** Push all the elements of another VArray onto the end of this one. + */ + void + Push_VArray(VArray *self, VArray *other); + + /** Pop an item off of the end of a VArray. + */ + incremented nullable Obj* + Pop(VArray *self); + + /** Unshift an item onto the front of a VArray. + */ + void + Unshift(VArray *self, decremented Obj *element = NULL); + + /** Shift an item off of the front of a VArray. + */ + incremented nullable Obj* + Shift(VArray *self); + + /** Ensure that the VArray has room for at least capacity + * elements. + */ + void + Grow(VArray *self, uint32_t capacity); + + /** Fetch the element at tick. + */ + nullable Obj* + Fetch(VArray *self, uint32_t tick); + + /** Store an element at index tick, possibly displacing an + * existing element. + */ + void + Store(VArray *self, uint32_t tick, decremented Obj *elem = NULL); + + /** Replace an element in the VArray with NULL and return it. + * + * @return whatever was stored at tick. + */ + incremented nullable Obj* + Delete(VArray *self, uint32_t tick); + + /** Remove length elements from the array, starting at + * offset. Move elements over to fill in the gap. + */ + void + Excise(VArray *self, uint32_t offset, uint32_t length); + + /** Clone the VArray but merely increment the refcounts of its elements + * rather than clone them. + */ + incremented VArray* + Shallow_Copy(VArray *self); + + /** Dupe the VArray, cloning each internal element. + */ + public incremented VArray* + Clone(VArray *self); + + /** Quicksort the VArry using the supplied comparison routine. Safety + * checks are the responsibility of the caller. + * + * @param compare Comparison routine. The default uses Obj_Compare_To and + * sorts NULL elements towards the end. + * @param context Argument supplied to the comparison routine. + */ + void + Sort(VArray *self, Lucy_Sort_Compare_t compare = NULL, + void *context = NULL); + + /** Set the size for the VArray. If the new size is larger than the + * current size, grow the object to accommodate NULL elements; if smaller + * than the current size, decrement and discard truncated elements. + */ + void + Resize(VArray *self, uint32_t size); + + /** Empty the VArray. + */ + void + Clear(VArray *self); + + /** Accessor for size member. + */ + public uint32_t + Get_Size(VArray *self); + + /** Accessor for capacity member. + */ + uint32_t + Get_Capacity(VArray *self); + + /** Return all elements for which test returns true. + */ + public incremented VArray* + Gather(VArray *self, Lucy_VA_Gather_Test_t test, void *data); + + /** Return a new array consisting of elements from a contiguous slice. If + * the specified range is out of bounds, return an array with fewer + * elements -- potentially none. + * + * @param offset The index of the element to start at. + * @param length The maximum number of elements to slice. + */ + public incremented VArray* + Slice(VArray *self, uint32_t offset, uint32_t length); + + public bool_t + Equals(VArray *self, Obj *other); + + public incremented VArray* + Dump(VArray *self); + + public incremented VArray* + Load(VArray *self, Obj *dump); + + public void + Serialize(VArray *self, OutStream *outstream); + + public incremented VArray* + Deserialize(decremented VArray *self, InStream *instream); + + public void + Destroy(VArray *self); +} + + diff --git a/clownfish/core/Clownfish/VTable.c b/clownfish/core/Clownfish/VTable.c new file mode 100644 index 000000000..fe62c6734 --- /dev/null +++ b/clownfish/core/Clownfish/VTable.c @@ -0,0 +1,330 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_VTABLE +#define C_LUCY_OBJ +#define C_LUCY_CHARBUF +#define C_LUCY_METHOD +#define LUCY_USE_SHORT_NAMES +#define CHY_USE_SHORT_NAMES + +#include +#include + +#include "Clownfish/VTable.h" +#include "Clownfish/CharBuf.h" +#include "Clownfish/Err.h" +#include "Clownfish/Hash.h" +#include "Clownfish/LockFreeRegistry.h" +#include "Clownfish/Method.h" +#include "Clownfish/Num.h" +#include "Clownfish/VArray.h" +#include "Clownfish/Util/Atomic.h" +#include "Clownfish/Util/Memory.h" + +size_t VTable_offset_of_parent = offsetof(VTable, parent); + +// Remove spaces and underscores, convert to lower case. +static void +S_scrunch_charbuf(CharBuf *source, CharBuf *target); + +LockFreeRegistry *VTable_registry = NULL; + +VTable* +VTable_allocate(VTable *parent, int flags, size_t obj_alloc_size, + size_t num_novel) { + size_t vt_alloc_size = parent + ? parent->vt_alloc_size + : offsetof(cfish_VTable, method_ptrs); + vt_alloc_size += num_novel * sizeof(cfish_method_t); + VTable *self = (VTable*)Memory_wrapped_calloc(vt_alloc_size, 1); + self->ref.count = 1; + self->parent = parent; + self->flags = flags; + self->obj_alloc_size = obj_alloc_size; + self->vt_alloc_size = vt_alloc_size; + + return self; +} + +VTable* +VTable_init(VTable *self, const CharBuf *name) { + self->vtable = CFISH_VTABLE; + self->name = CB_Clone(name); + self->methods = VA_new(0); + + VTable *parent = self->parent; + if (parent) { + size_t parent_ptrs_size = parent->vt_alloc_size + - offsetof(cfish_VTable, method_ptrs); + memcpy(self->method_ptrs, parent->method_ptrs, parent_ptrs_size); + } + + return self; +} + +void +VTable_add_method(VTable *self, const CharBuf *name, + lucy_method_t callback_func, size_t offset) { + Method *method = Method_new(name, callback_func, offset); + VA_Push(self->methods, (Obj*)method); +} + +void +VTable_destroy(VTable *self) { + THROW(ERR, "Insane attempt to destroy VTable for class '%o'", self->name); +} + +VTable* +VTable_clone(VTable *self) { + VTable *twin + = (VTable*)Memory_wrapped_calloc(self->vt_alloc_size, 1); + + memcpy(twin, self, self->vt_alloc_size); + twin->name = CB_Clone(self->name); + twin->ref.count = 1; + + return twin; +} + +Obj* +VTable_inc_refcount(VTable *self) { + return (Obj*)self; +} + +uint32_t +VTable_dec_refcount(VTable *self) { + UNUSED_VAR(self); + return 1; +} + +uint32_t +VTable_get_refcount(VTable *self) { + UNUSED_VAR(self); + /* VTable_Get_RefCount() lies to other Lucy code about the refcount + * because we don't want to have to synchronize access to the cached host + * object to which we have delegated responsibility for keeping refcounts. + * It always returns 1 because 1 is a positive number, and thus other Lucy + * code will be fooled into believing it never needs to take action such + * as initiating a destructor. + * + * It's possible that the host has in fact increased the refcount of the + * cached host object if there are multiple refs to it on the other side + * of the Lucy/host border, but returning 1 is good enough to fool Lucy + * code. + */ + return 1; +} + +void +VTable_override(VTable *self, lucy_method_t method, size_t offset) { + union { char *char_ptr; lucy_method_t *func_ptr; } pointer; + pointer.char_ptr = ((char*)self) + offset; + pointer.func_ptr[0] = method; +} + +CharBuf* +VTable_get_name(VTable *self) { + return self->name; +} + +VTable* +VTable_get_parent(VTable *self) { + return self->parent; +} + +size_t +VTable_get_obj_alloc_size(VTable *self) { + return self->obj_alloc_size; +} + +void +VTable_init_registry() { + LockFreeRegistry *reg = LFReg_new(256); + if (Atomic_cas_ptr((void*volatile*)&VTable_registry, NULL, reg)) { + return; + } + else { + DECREF(reg); + } +} + +VTable* +VTable_singleton(const CharBuf *class_name, VTable *parent) { + if (VTable_registry == NULL) { + VTable_init_registry(); + } + + VTable *singleton = (VTable*)LFReg_Fetch(VTable_registry, (Obj*)class_name); + if (singleton == NULL) { + VArray *fresh_host_methods; + uint32_t num_fresh; + + if (parent == NULL) { + CharBuf *parent_class = VTable_find_parent_class(class_name); + if (parent_class == NULL) { + THROW(ERR, "Class '%o' doesn't descend from %o", class_name, + OBJ->name); + } + else { + parent = VTable_singleton(parent_class, NULL); + DECREF(parent_class); + } + } + + // Copy source vtable. + singleton = VTable_Clone(parent); + + // Turn clone into child. + singleton->parent = parent; + DECREF(singleton->name); + singleton->name = CB_Clone(class_name); + + // Allow host methods to override. + fresh_host_methods = VTable_fresh_host_methods(class_name); + num_fresh = VA_Get_Size(fresh_host_methods); + if (num_fresh) { + Hash *meths = Hash_new(num_fresh); + CharBuf *scrunched = CB_new(0); + ZombieCharBuf *callback_name = ZCB_BLANK(); + for (uint32_t i = 0; i < num_fresh; i++) { + CharBuf *meth = (CharBuf*)VA_fetch(fresh_host_methods, i); + S_scrunch_charbuf(meth, scrunched); + Hash_Store(meths, (Obj*)scrunched, (Obj*)CFISH_TRUE); + } + for (VTable *vtable = parent; vtable; vtable = vtable->parent) { + uint32_t max = VA_Get_Size(vtable->methods); + for (uint32_t i = 0; i < max; i++) { + Method *method = (Method*)VA_Fetch(vtable->methods, i); + if (method->callback_func) { + S_scrunch_charbuf(method->name, scrunched); + if (Hash_Fetch(meths, (Obj*)scrunched)) { + VTable_Override(singleton, method->callback_func, + method->offset); + } + } + } + } + DECREF(scrunched); + DECREF(meths); + } + DECREF(fresh_host_methods); + + // Register the new class, both locally and with host. + if (VTable_add_to_registry(singleton)) { + // Doing this after registering is racy, but hard to fix. :( + VTable_register_with_host(singleton, parent); + } + else { + DECREF(singleton); + singleton = (VTable*)LFReg_Fetch(VTable_registry, (Obj*)class_name); + if (!singleton) { + THROW(ERR, "Failed to either insert or fetch VTable for '%o'", + class_name); + } + } + } + + return singleton; +} + +Obj* +VTable_make_obj(VTable *self) { + Obj *obj = (Obj*)Memory_wrapped_calloc(self->obj_alloc_size, 1); + obj->vtable = self; + obj->ref.count = 1; + return obj; +} + +Obj* +VTable_init_obj(VTable *self, void *allocation) { + Obj *obj = (Obj*)allocation; + obj->vtable = self; + obj->ref.count = 1; + return obj; +} + +Obj* +VTable_load_obj(VTable *self, Obj *dump) { + Obj_Load_t load = METHOD_PTR(self, Lucy_Obj_Load); + if (load == Obj_load) { + THROW(ERR, "Abstract method Load() not defined for %o", self->name); + } + Obj *invoker = VTable_Make_Obj(self); + Obj *loaded = load(invoker, dump); + DECREF(invoker); + return loaded; +} + +static void +S_scrunch_charbuf(CharBuf *source, CharBuf *target) { + ZombieCharBuf *iterator = ZCB_WRAP(source); + CB_Set_Size(target, 0); + while (ZCB_Get_Size(iterator)) { + uint32_t code_point = ZCB_Nip_One(iterator); + if (code_point > 127) { + THROW(ERR, "Can't fold case for %o", source); + } + else if (code_point != '_') { + CB_Cat_Char(target, tolower(code_point)); + } + } +} + +bool_t +VTable_add_to_registry(VTable *vtable) { + if (VTable_registry == NULL) { + VTable_init_registry(); + } + if (LFReg_Fetch(VTable_registry, (Obj*)vtable->name)) { + return false; + } + else { + CharBuf *klass = CB_Clone(vtable->name); + bool_t retval + = LFReg_Register(VTable_registry, (Obj*)klass, (Obj*)vtable); + DECREF(klass); + return retval; + } +} + +bool_t +VTable_add_alias_to_registry(VTable *vtable, CharBuf *alias) { + if (VTable_registry == NULL) { + VTable_init_registry(); + } + if (LFReg_Fetch(VTable_registry, (Obj*)alias)) { + return false; + } + else { + CharBuf *klass = CB_Clone(alias); + bool_t retval + = LFReg_Register(VTable_registry, (Obj*)klass, (Obj*)vtable); + DECREF(klass); + return retval; + } +} + +VTable* +VTable_fetch_vtable(const CharBuf *class_name) { + VTable *vtable = NULL; + if (VTable_registry != NULL) { + vtable = (VTable*)LFReg_Fetch(VTable_registry, (Obj*)class_name); + } + return vtable; +} + + diff --git a/clownfish/core/Clownfish/VTable.cfh b/clownfish/core/Clownfish/VTable.cfh new file mode 100644 index 000000000..6308d7b8a --- /dev/null +++ b/clownfish/core/Clownfish/VTable.cfh @@ -0,0 +1,157 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcel Lucy; + +/** Virtual method dispatch table. + * + * VTables, which are the first element in any Clownfish object, are actually + * objects themselves. (Their first element is a VTable which describes the + * behavior of VTables.) + */ + +class Clownfish::VTable inherits Clownfish::Obj { + + VTable *parent; + CharBuf *name; + uint32_t flags; + size_t obj_alloc_size; + size_t vt_alloc_size; + VArray *methods; + lucy_method_t[1] method_ptrs; /* flexible array */ + + inert LockFreeRegistry *registry; + inert size_t offset_of_parent; + + inert VTable* + allocate(VTable *parent, int flags, size_t obj_alloc_size, + size_t num_novel); + + inert VTable* + init(VTable *self, const CharBuf *name); + + inert void + add_method(VTable *self, const CharBuf *name, lucy_method_t callback_func, + size_t offset); + + /** Return a singleton. If a VTable can be found in the registry based on + * the supplied class name, it will be returned. Otherwise, a new VTable + * will be created using [parent] as a base. + * + * If [parent] is NULL, an attempt will be made to find it using + * VTable_find_parent_class(). If the attempt fails, an error will + * result. + */ + inert VTable* + singleton(const CharBuf *class_name, VTable *parent); + + /** Register a vtable, so that it can be retrieved by class name. + * + * TODO: Move this functionality to some kind of class loader. + * + * @return true on success, false if the class was already registered. + */ + inert bool_t + add_to_registry(VTable *vtable); + + inert bool_t + add_alias_to_registry(VTable *vtable, CharBuf *alias); + + /** Initialize the registry. + */ + inert void + init_registry(); + + /** Tell the host about the new class. + */ + inert void + register_with_host(VTable *vtable, VTable *parent); + + /** Find a registered class and return its vtable. May return NULL if the + * class is not registered. + */ + inert nullable VTable* + fetch_vtable(const CharBuf *class_name); + + /** Given a class name, return the name of a parent class which descends + * from Clownfish::Obj, or NULL if such a class can't be found. + */ + inert nullable CharBuf* + find_parent_class(const CharBuf *class_name); + + /** List all of the methods defined directly within a host subclass. + */ + inert incremented VArray* + fresh_host_methods(const CharBuf *class_name); + + /** Replace a function pointer in the VTable. + */ + void + Override(VTable *self, lucy_method_t method_ptr, size_t offset); + + /** Create an empty object of the type defined by the VTable: allocate, + * assign its vtable and give it an initial refcount of 1. The caller is + * responsible for initialization. + */ + Obj* + Make_Obj(VTable *self); + + /** Take a raw memory allocation which is presumed to be of adequate size, + * assign its vtable and give it an initial refcount of 1. + */ + Obj* + Init_Obj(VTable *self, void *allocation); + + /** Create a new object using the supplied dump, assuming that Load() has + * been defined for the class. + */ + Obj* + Load_Obj(VTable *self, Obj *dump); + + /** Create a new object to go with the supplied host object. + */ + Obj* + Foster_Obj(VTable *self, void *host_obj); + + CharBuf* + Get_Name(VTable *self); + + VTable* + Get_Parent(VTable *self); + + size_t + Get_Obj_Alloc_Size(VTable *self); + + public incremented VTable* + Clone(VTable *self); + + incremented Obj* + Inc_RefCount(VTable *self); + + uint32_t + Dec_RefCount(VTable *self); + + uint32_t + Get_RefCount(VTable *self); + + void* + To_Host(VTable *self); + + public void + Destroy(VTable *self); +} + + diff --git a/clownfish/perl/Build.PL b/clownfish/perl/Build.PL new file mode 100644 index 000000000..6b25c44e1 --- /dev/null +++ b/clownfish/perl/Build.PL @@ -0,0 +1,84 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use 5.008003; +use strict; +use warnings; +use lib 'buildlib'; +use File::Spec::Functions qw( catdir ); +use Lucy::Build; + +my @BASE_PATH = Lucy::Build->cf_base_path; +my $MODULES_DIR = catdir( @BASE_PATH, 'modules' ); +my $SNOWSTEM_SRC_DIR = catdir( $MODULES_DIR, qw( analysis snowstem source ) ); +my $SNOWSTEM_INC_DIR = catdir( $SNOWSTEM_SRC_DIR, 'include' ); +my $SNOWSTOP_SRC_DIR = catdir( $MODULES_DIR, qw( analysis snowstop source ) ); +my $UCD_INC_DIR = catdir( $MODULES_DIR, qw( unicode ucd ) ); +my $UTF8PROC_SRC_DIR = catdir( $MODULES_DIR, qw( unicode utf8proc ) ); +my $CORE_SOURCE_DIR = catdir( @BASE_PATH, 'core' ); +my $XS_SOURCE_DIR = 'xs'; + +my $builder = Lucy::Build->new( + module_name => 'Lucy', + license => 'apache', + dist_author => + 'The Apache Lucy Project ', + dist_version => '0.3.0', + requires => { 'perl' => '5.8.3', }, + configure_requires => { 'Module::Build' => 0.280801 }, + build_requires => { + 'Module::Build' => 0.280801, + 'ExtUtils::CBuilder' => 0.21, + 'ExtUtils::ParseXS' => 2.18, + 'Devel::PPPort' => 3.13, + }, + meta_merge => { keywords => [qw( search lucy lucene )], }, + meta_add => { + resources => { + homepage => 'http://lucy.apache.org', + repository => 'http://svn.apache.org/repos/asf/lucy', + bugtracker => 'https://issues.apache.org/jira/browse/LUCY', + }, + }, + include_dirs => [ + $CORE_SOURCE_DIR, + $XS_SOURCE_DIR, + $SNOWSTEM_INC_DIR, + $UCD_INC_DIR, + $UTF8PROC_SRC_DIR, + ], + clownfish_params => { + source => [ + $CORE_SOURCE_DIR, + $XS_SOURCE_DIR, + $SNOWSTEM_SRC_DIR, + $SNOWSTOP_SRC_DIR, + $UTF8PROC_SRC_DIR, + ], + }, + add_to_cleanup => [ + qw( + Lucy-* + MANIFEST.bak + perltidy.ERR + *.pdb + *.manifest + ), + ], +); + +$builder->create_build_script(); + +__END__ diff --git a/clownfish/perl/MANIFEST b/clownfish/perl/MANIFEST new file mode 100644 index 000000000..b156cfaaa --- /dev/null +++ b/clownfish/perl/MANIFEST @@ -0,0 +1,7 @@ +# If you opened this file because "./Build distclean" spawned numerous +# about files not present in MANIFEST, the solution is to run "./Build +# realclean" instead. +# +# We do not maintain MANIFEST during development; if necessary, it will be +# autogenerated when we build the CPAN tarball. + diff --git a/clownfish/perl/buildlib/Clownfish/Build.pm b/clownfish/perl/buildlib/Clownfish/Build.pm new file mode 100644 index 000000000..006ffba29 --- /dev/null +++ b/clownfish/perl/buildlib/Clownfish/Build.pm @@ -0,0 +1,550 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; + +use lib '../clownfish/cfc/perl/blib/arch'; +use lib '../clownfish/cfc/perl/blib/lib'; +use lib 'clownfish/cfc/perl/blib/arch'; +use lib 'clownfish/cfc/perl/blib/lib'; + +package Lucy::Build; + +# We want to subclass Clownfish::CFC::Perl::Build, but Clownfish might not be +# built yet. So we look in 'clownfish/cfc/perl/lib' directly and cleanup @INC +# afterwards. +use lib '../clownfish/cfc/perl/lib'; +use lib 'clownfish/cfc/perl/lib'; +use base qw( Clownfish::CFC::Perl::Build ); +no lib '../clownfish/cfc/perl/lib'; +no lib 'clownfish/cfc/perl/lib'; + +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +use File::Spec::Functions qw( catdir catfile updir rel2abs ); +use File::Path qw( rmtree ); +use File::Copy qw( move ); +use Config; +use Env qw( @PATH ); +use Carp; +use Cwd qw( getcwd ); + +BEGIN { unshift @PATH, rel2abs( getcwd() ) } + +my @BASE_PATH = __PACKAGE__->cf_base_path; + +my $CHARMONIZER_ORIG_DIR = catdir( @BASE_PATH, 'charmonizer' ); +my $CHARMONIZE_EXE_PATH = "charmonize$Config{_exe}"; +my $CHARMONY_H_PATH = 'charmony.h'; +my $CHARMONY_PM_PATH = 'Charmony.pm'; +my $LEMON_DIR = catdir( @BASE_PATH, 'lemon' ); +my $LEMON_EXE_PATH = catfile( $LEMON_DIR, "lemon$Config{_exe}" ); +my $CORE_SOURCE_DIR = catdir( @BASE_PATH, 'core' ); +my $CLOWNFISH_DIR = catdir( @BASE_PATH, 'clownfish', 'cfc', 'perl' ); +my $CLOWNFISH_BUILD = catfile( $CLOWNFISH_DIR, 'Build' ); +my $LIB_DIR = 'lib'; + +sub new { + my $self = shift->SUPER::new( recursive_test_files => 1, @_ ); + + if ( $ENV{LUCY_VALGRIND} ) { + my $optimize = $self->config('optimize') || ''; + $optimize =~ s/\-O\d+/-O1/g; + $self->config( optimize => $optimize ); + } + + my $extra_ccflags = $self->extra_compiler_flags; + if ( $self->config('gccversion') ) { + if ( $Config{osname} =~ /openbsd/i && !$Config{usethreads} ) { + push @$extra_ccflags, '-DLUCY_NOTHREADS'; + } + if ( defined $ENV{LUCY_VALGRIND} ) { + push @$extra_ccflags, qw( -DLUCY_VALGRIND -fno-inline-functions ); + } + elsif ( defined $ENV{LUCY_DEBUG} ) { + push @$extra_ccflags, qw( + -DLUCY_DEBUG -pedantic -Wall -Wextra -Wno-variadic-macros + ); + } + } + $self->extra_compiler_flags(@$extra_ccflags); + + $self->clownfish_params( autogen_header => $self->autogen_header ); + + return $self; +} + +sub _run_make { + my ( $self, %params ) = @_; + my @command = @{ $params{args} }; + my $dir = $params{dir}; + my $current_directory = getcwd(); + chdir $dir if $dir; + unshift @command, 'CC=' . $self->config('cc'); + if ( $self->config('cc') =~ /^cl\b/ ) { + unshift @command, "-f", "Makefile.MSVC"; + } + elsif ( $^O =~ /mswin/i ) { + unshift @command, "-f", "Makefile.MinGW"; + } + unshift @command, "$Config{make}"; + system(@command) and confess("$Config{make} failed"); + chdir $current_directory if $dir; +} + +# Build the charmonize executable. +sub ACTION_build_charmonize { + my $self = shift; + print "Building $CHARMONIZE_EXE_PATH...\n\n"; + my $meld_c = rel2abs("charmonize.c"); + $self->add_to_cleanup($meld_c); + $self->add_to_cleanup($CHARMONIZE_EXE_PATH); + my $charmonize_main = catfile( $CHARMONIZER_ORIG_DIR, 'charmonize.c' ); + $self->_run_make( + dir => $CHARMONIZER_ORIG_DIR, + args => [ "meld", "PERL=$^X", "FILES=$charmonize_main", "OUT=$meld_c" ], + ); + if ( !$self->up_to_date( $meld_c, $CHARMONIZE_EXE_PATH ) ) { + my $cc = $Config{cc}; + my $outflag = $cc =~ /cl\b/ ? "/Fe" : "-o "; + system("$cc $meld_c $outflag$CHARMONIZE_EXE_PATH") + and die "Failed to compile $CHARMONIZE_EXE_PATH"; + } +} + +# Run the charmonize executable, creating the charmony.h and Charmony.pm +# files. +sub ACTION_run_charmonize { + my $self = shift; + $self->dispatch('build_charmonize'); + return if $self->up_to_date( $CHARMONIZE_EXE_PATH, [ + $CHARMONY_H_PATH, $CHARMONY_PM_PATH, + ] ); + print "\nRunning $CHARMONIZE_EXE_PATH...\n\n"; + + $self->add_to_cleanup($CHARMONY_H_PATH); + $self->add_to_cleanup($CHARMONY_PM_PATH); + # Clean up after charmonize if it doesn't succeed on its own. + $self->add_to_cleanup("_charm*"); + + # Prepare arguments to charmonize. + my @command = ( + $CHARMONIZE_EXE_PATH, + '--cc=' . _quotify( $self->config('cc') ), + '--enable-c', + '--enable-perl', + '--', + $self->config('ccflags'), + @{ $self->extra_compiler_flags }, + ); + if ( $ENV{CHARM_VALGRIND} ) { + unshift @command, "valgrind", "--leak-check=yes"; + } + print join( " ", @command ), $/; + + system(@command) and die "Failed to run $CHARMONIZE_EXE_PATH: $!"; +} + +sub _quotify { + my $string = shift; + $string =~ s/\\/\\\\/g; + $string =~ s/"/\\"/g; + return qq|"$string"|; +} + +# Build the charmonizer tests. +sub ACTION_charmonizer_tests { + my $self = shift; + $self->dispatch('run_charmonize'); + print "Building Charmonizer Tests...\n\n"; + my $flags = join( " ", + $self->config('ccflags'), + @{ $self->extra_compiler_flags }, + '-I' . rel2abs( getcwd() ), + ); + $flags =~ s/"/\\"/g; + $self->_run_make( + dir => $CHARMONIZER_ORIG_DIR, + args => [ "DEFS=$flags", "tests" ], + ); +} + +# Build the Lemon parser generator. +sub ACTION_lemon { + my $self = shift; + print "Building the Lemon parser generator...\n\n"; + $self->_run_make( + dir => $LEMON_DIR, + args => [], + ); +} + +sub ACTION_cfc { + my $self = shift; + my $old_dir = getcwd(); + chdir($CLOWNFISH_DIR); + if ( !-f 'Build' ) { + print "\nBuilding Clownfish compiler... \n"; + system("$^X Build.PL"); + system("$^X Build code"); + print "\nFinished building Clownfish compiler.\n\n"; + } + chdir($old_dir); +} + +sub ACTION_copy_clownfish_includes { + my $self = shift; + + $self->dispatch('run_charmonize'); + + $self->SUPER::ACTION_copy_clownfish_includes; + + $self->cf_copy_include_file( 'charmony.h' ); + $self->cf_copy_include_file( 'XSBind.h' ); + $self->cf_copy_include_file( qw( Lucy Util ToolSet.h ) ); +} + +sub ACTION_clownfish { + my $self = shift; + + $self->dispatch('charmonizer_tests'); + $self->dispatch('cfc'); + + $self->SUPER::ACTION_clownfish; +} + +sub ACTION_suppressions { + my $self = shift; + my $LOCAL_SUPP = 'local.supp'; + return + if $self->up_to_date( '../devel/bin/valgrind_triggers.pl', + $LOCAL_SUPP ); + + # Generate suppressions. + print "Writing $LOCAL_SUPP...\n"; + $self->add_to_cleanup($LOCAL_SUPP); + my $command + = "yes | " + . $self->_valgrind_base_command + . "--gen-suppressions=yes " + . $self->perl + . " ../devel/bin/valgrind_triggers.pl 2>&1"; + my $suppressions = `$command`; + $suppressions =~ s/^==.*?\n//mg; + $suppressions =~ s/^--.*?\n//mg; + my $rule_number = 1; + + while ( $suppressions =~ // ) { + $suppressions =~ s/^\s*/{\n /m; + $rule_number++; + } + + # Change e.g. fun:_vgrZU_libcZdsoZa_calloc to fun:calloc + $suppressions =~ s/fun:\w+_((m|c|re)alloc)/fun:$1/g; + + # Write local suppressions file. + open( my $supp_fh, '>', $LOCAL_SUPP ) + or confess("Can't open '$LOCAL_SUPP': $!"); + print $supp_fh $suppressions; +} + +sub _valgrind_base_command { + return + "PERL_DESTRUCT_LEVEL=2 LUCY_VALGRIND=1 valgrind " + . "--leak-check=yes " + . "--show-reachable=yes " + . "--num-callers=10 " + . "--dsymutil=yes " + . "--suppressions=../devel/conf/lucyperl.supp "; +} + +# Run the entire test suite under Valgrind. +# +# For this to work, Lucy must be compiled with the LUCY_VALGRIND environment +# variable set to a true value, under a debugging Perl. +# +# A custom suppressions file will probably be needed -- use your judgment. +# To pass in one or more local suppressions files, provide a comma separated +# list like so: +# +# $ ./Build test_valgrind --suppressions=foo.supp,bar.supp +sub ACTION_test_valgrind { + my $self = shift; + die "Must be run under a perl that was compiled with -DDEBUGGING" + unless $self->config('ccflags') =~ /-D?DEBUGGING\b/; + if ( !$ENV{LUCY_VALGRIND} ) { + warn "\$ENV{LUCY_VALGRIND} not true -- possible false positives"; + } + $self->dispatch('code'); + $self->dispatch('suppressions'); + + # Unbuffer STDOUT, grab test file names and suppressions files. + $|++; + my $t_files = $self->find_test_files; # not public M::B API, may fail + my $valgrind_command = $self->_valgrind_base_command; + $valgrind_command .= "--suppressions=local.supp "; + + if ( my $local_supp = $self->args('suppressions') ) { + for my $supp ( split( ',', $local_supp ) ) { + $valgrind_command .= "--suppressions=$supp "; + } + } + + # Iterate over test files. + my @failed; + for my $t_file (@$t_files) { + + # Run test file under Valgrind. + print "Testing $t_file..."; + die "Can't find '$t_file'" unless -f $t_file; + my $command = "$valgrind_command $^X -Mblib $t_file 2>&1"; + my $output = "\n" . ( scalar localtime(time) ) . "\n$command\n"; + $output .= `$command`; + + # Screen-scrape Valgrind output, looking for errors and leaks. + if ( $? + or $output =~ /ERROR SUMMARY:\s+[^0\s]/ + or $output =~ /definitely lost:\s+[^0\s]/ + or $output =~ /possibly lost:\s+[^0\s]/ + or $output =~ /still reachable:\s+[^0\s]/ ) + { + print " failed.\n"; + push @failed, $t_file; + print "$output\n"; + } + else { + print " succeeded.\n"; + } + } + + # If there are failed tests, print a summary list. + if (@failed) { + print "\nFailed " + . scalar @failed . "/" + . scalar @$t_files + . " test files:\n " + . join( "\n ", @failed ) . "\n"; + exit(1); + } +} + +# Run all .y files through lemon. +sub ACTION_parsers { + my $self = shift; + $self->dispatch('lemon'); + my $y_files = $self->rscan_dir( $CORE_SOURCE_DIR, qr/\.y$/ ); + for my $y_file (@$y_files) { + my $c_file = $y_file; + my $h_file = $y_file; + $c_file =~ s/\.y$/.c/ or die "no match"; + $h_file =~ s/\.y$/.h/ or die "no match"; + next if $self->up_to_date( $y_file, [ $c_file, $h_file ] ); + $self->add_to_cleanup( $c_file, $h_file ); + system( $LEMON_EXE_PATH, '-q', $y_file ) and die "lemon failed"; + } +} + +sub ACTION_compile_custom_xs { + my $self = shift; + + $self->dispatch('parsers'); + + $self->SUPER::ACTION_compile_custom_xs; +} + +sub autogen_header { + my $self = shift; + return <<"END_AUTOGEN"; +/*********************************************** + + !!!! DO NOT EDIT !!!! + + This file was auto-generated by Build.PL. + + ***********************************************/ + +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +END_AUTOGEN +} + +sub _check_module_build_for_dist { + eval "use Module::Build 0.38;"; + die "./Build dist reqiures Module::Build 0.38 or higher--this is only " + . Module::Build->VERSION . $/ if $@; +} + +sub ACTION_distdir { + _check_module_build_for_dist; + shift->SUPER::ACTION_distdir(@_); +} + +sub ACTION_dist { + my $self = shift; + _check_module_build_for_dist; + + # Create POD but make sure not to include build artifacts. + $self->dispatch('pod'); + _clean_prereq_builds($self); + + # We build our Perl release tarball from $REPOS_ROOT/perl, rather than + # from the top-level. + # + # Because some items we need are outside this directory, we need to copy a + # bunch of stuff. After the tarball is packaged up, we delete the copied + # directories. + my @items_to_copy = qw( + core + modules + charmonizer + devel + clownfish + lemon + CHANGES + CONTRIBUTING + LICENSE + NOTICE + README + ); + print "Copying files...\n"; + + for my $item (@items_to_copy) { + confess("'$item' already exists") if -e $item; + system("cp -R ../$item $item"); + } + + $self->dispatch('manifest'); + my $no_index = $self->_gen_pause_exclusion_list; + my $meta_add = $self->meta_add || {}; + $meta_add->{no_index} = $no_index; + $self->meta_add( $meta_add ); + $self->SUPER::ACTION_dist; + + # Clean up. + print "Removing copied files...\n"; + rmtree($_) for @items_to_copy; + unlink("META.yml"); + move( "MANIFEST.bak", "MANIFEST" ) or die "move() failed: $!"; +} + +sub ACTION_distmeta { + my $self = shift; + $self->SUPER::ACTION_distmeta(@_); + # Make sure everything has a version. + require CPAN::Meta; + my $v = version->new($self->dist_version); + my $meta = CPAN::Meta->load_file('META.json'); + my $provides = $meta->provides; + while (my ($pkg, $data) = each %{ $provides }) { + die "$pkg, defined in $data->{file}, has no version\n" + unless $data->{version}; + die "$pkg, defined in $data->{file}, is " + . version->new($data->{version})->normal + . " but should be " . $v->normal . "\n" + unless $data->{version} == $v; + } +} + +# Generate a list of files for PAUSE, search.cpan.org, etc to ignore. +sub _gen_pause_exclusion_list { + my $self = shift; + + # Only exclude files that are actually on-board. + open( my $man_fh, '<', 'MANIFEST' ) or die "Can't open MANIFEST: $!"; + my @manifest_entries = <$man_fh>; + chomp @manifest_entries; + + my @excluded_files; + for my $entry (@manifest_entries) { + # Allow README and Changes. + next if $entry =~ m#^(README|Changes)#; + + # Allow public modules. + if ( $entry =~ m#^(perl/)?lib\b.+\.(pm|pod)$# ) { + open( my $fh, '<', $entry ) or die "Can't open '$entry': $!"; + my $content = do { local $/; <$fh> }; + next if $content =~ /=head1\s*NAME/; + } + + # Disallow everything else. + push @excluded_files, $entry; + } + + # Exclude redacted modules. + if ( eval { require "buildlib/Lucy/Redacted.pm" } ) { + my @redacted = map { + my @parts = split( /\W+/, $_ ); + catfile( $LIB_DIR, @parts ) . '.pm' + } Lucy::Redacted->redacted, Lucy::Redacted->hidden; + push @excluded_files, @redacted; + } + + my %uniquifier; + @excluded_files = sort grep { !$uniquifier{$_}++ } @excluded_files; + return { file => \@excluded_files }; +} + +sub ACTION_semiclean { + my $self = shift; + print "Cleaning up most build files.\n"; + my @candidates + = grep { $_ !~ /(charmonizer|^_charm|charmony|charmonize|snowstem)/ } + $self->cleanup; + for my $path ( map { glob($_) } @candidates ) { + next unless -e $path; + rmtree($path); + confess("Failed to remove '$path'") if -e $path; + } +} + +# Run the cleanup targets for independent prerequisite builds. +sub _clean_prereq_builds { + my $self = shift; + if ( -e $CLOWNFISH_BUILD ) { + my $old_dir = getcwd(); + chdir $CLOWNFISH_DIR; + system("$^X Build realclean") + and die "Clownfish clean failed"; + chdir $old_dir; + } + $self->_run_make( dir => $CHARMONIZER_ORIG_DIR, args => ['clean'] ); + $self->_run_make( dir => $LEMON_DIR, args => ['clean'] ); +} + +sub ACTION_clean { + my $self = shift; + _clean_prereq_builds($self); + $self->SUPER::ACTION_clean; +} + +1; + +__END__ diff --git a/clownfish/perl/buildlib/Clownfish/Build/Binding.pm b/clownfish/perl/buildlib/Clownfish/Build/Binding.pm new file mode 100644 index 000000000..095129584 --- /dev/null +++ b/clownfish/perl/buildlib/Clownfish/Build/Binding.pm @@ -0,0 +1,881 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +package Lucy::Build::Binding::Object; +use strict; +use warnings; + +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +sub bind_all { + my $class = shift; + $class->bind_bitvector; + $class->bind_bytebuf; + $class->bind_charbuf; + $class->bind_err; + $class->bind_hash; + $class->bind_host; + $class->bind_i32array; + $class->bind_lockfreeregistry; + $class->bind_float32; + $class->bind_float64; + $class->bind_obj; + $class->bind_varray; + $class->bind_vtable; +} + +sub bind_bitvector { + my @exposed = qw( + Get + Set + Clear + Clear_All + And + Or + And_Not + Xor + Flip + Flip_Block + Next_Hit + To_Array + Grow + Count + ); + + my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; + my $synopsis = <<'END_SYNOPSIS'; + my $bit_vec = Lucy::Object::BitVector->new( capacity => 8 ); + my $other = Lucy::Object::BitVector->new( capacity => 8 ); + $bit_vec->set($_) for ( 0, 2, 4, 6 ); + $other->set($_) for ( 1, 3, 5, 7 ); + $bit_vec->or($other); + print "$_\n" for @{ $bit_vec->to_array }; # prints 0 through 7. +END_SYNOPSIS + my $constructor = <<'END_CONSTRUCTOR'; + my $bit_vec = Lucy::Object::BitVector->new( + capacity => $doc_max + 1, # default 0, + ); +END_CONSTRUCTOR + $pod_spec->set_synopsis($synopsis); + $pod_spec->add_constructor( alias => 'new', sample => $constructor ); + $pod_spec->add_method( method => $_, alias => lc($_) ) for @exposed; + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Lucy::Object::BitVector", + ); + $binding->set_pod_spec($pod_spec); + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_bytebuf { + my $xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::ByteBuf + +SV* +new(either_sv, sv) + SV *either_sv; + SV *sv; +CODE: +{ + STRLEN size; + char *ptr = SvPV(sv, size); + lucy_ByteBuf *self = (lucy_ByteBuf*)XSBind_new_blank_obj(either_sv); + lucy_BB_init(self, size); + Lucy_BB_Mimic_Bytes(self, ptr, size); + RETVAL = CFISH_OBJ_TO_SV_NOINC(self); +} +OUTPUT: RETVAL + +SV* +_deserialize(self, instream) + lucy_ByteBuf *self; + lucy_InStream *instream; +CODE: + lucy_ByteBuf *thawed = Lucy_BB_Deserialize(self, instream); + RETVAL = (SV*)Lucy_BB_To_Host(thawed); +OUTPUT: RETVAL +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::ByteBuf", + ); + $binding->append_xs($xs_code); + $binding->exclude_constructor; + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_charbuf { + my $xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::CharBuf + +SV* +new(either_sv, sv) + SV *either_sv; + SV *sv; +CODE: +{ + STRLEN size; + char *ptr = SvPVutf8(sv, size); + lucy_CharBuf *self = (lucy_CharBuf*)XSBind_new_blank_obj(either_sv); + lucy_CB_init(self, size); + Lucy_CB_Cat_Trusted_Str(self, ptr, size); + RETVAL = CFISH_OBJ_TO_SV_NOINC(self); +} +OUTPUT: RETVAL + +SV* +_clone(self) + lucy_CharBuf *self; +CODE: + RETVAL = CFISH_OBJ_TO_SV_NOINC(lucy_CB_clone(self)); +OUTPUT: RETVAL + +SV* +_deserialize(self, instream) + lucy_CharBuf *self; + lucy_InStream *instream; +CODE: + lucy_CharBuf *thawed = Lucy_CB_Deserialize(self, instream); + RETVAL = (SV*)Lucy_CB_To_Host(thawed); +OUTPUT: RETVAL + +SV* +to_perl(self) + lucy_CharBuf *self; +CODE: + RETVAL = XSBind_cb_to_sv(self); +OUTPUT: RETVAL + +MODULE = Lucy PACKAGE = Clownfish::ViewCharBuf + +SV* +_new(unused, sv) + SV *unused; + SV *sv; +CODE: +{ + STRLEN size; + char *ptr = SvPVutf8(sv, size); + lucy_ViewCharBuf *self + = lucy_ViewCB_new_from_trusted_utf8(ptr, size); + CHY_UNUSED_VAR(unused); + RETVAL = CFISH_OBJ_TO_SV_NOINC(self); +} +OUTPUT: RETVAL +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::CharBuf", + ); + $binding->append_xs($xs_code); + $binding->exclude_constructor; + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_err { + my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; + my $synopsis = <<'END_SYNOPSIS'; + package MyErr; + use base qw( Clownfish::Err ); + + ... + + package main; + use Scalar::Util qw( blessed ); + while (1) { + eval { + do_stuff() or MyErr->throw("retry"); + }; + if ( blessed($@) and $@->isa("MyErr") ) { + warn "Retrying...\n"; + } + else { + # Re-throw. + die "do_stuff() died: $@"; + } + } +END_SYNOPSIS + $pod_spec->set_synopsis($synopsis); + + my $xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::Err + +SV* +trap(routine_sv, context_sv) + SV *routine_sv; + SV *context_sv; +CODE: + cfish_Err *error = XSBind_trap(routine_sv, context_sv); + RETVAL = CFISH_OBJ_TO_SV_NOINC(error); +OUTPUT: RETVAL +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::Err", + ); + $binding->bind_constructor( alias => '_new' ); + $binding->set_pod_spec($pod_spec); + $binding->append_xs($xs_code); + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_hash { + my @hand_rolled = qw( + Store + Next + ); + + my $xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::Hash + +SV* +_deserialize(self, instream) + lucy_Hash *self; + lucy_InStream *instream; +CODE: + lucy_Hash *thawed = Lucy_Hash_Deserialize(self, instream); + RETVAL = (SV*)Lucy_Hash_To_Host(thawed); +OUTPUT: RETVAL + +SV* +_fetch(self, key) + lucy_Hash *self; + const lucy_CharBuf *key; +CODE: + RETVAL = CFISH_OBJ_TO_SV(lucy_Hash_fetch(self, (lucy_Obj*)key)); +OUTPUT: RETVAL + +void +store(self, key, value); + lucy_Hash *self; + const lucy_CharBuf *key; + lucy_Obj *value; +PPCODE: +{ + if (value) { CFISH_INCREF(value); } + lucy_Hash_store(self, (lucy_Obj*)key, value); +} + +void +next(self) + lucy_Hash *self; +PPCODE: +{ + lucy_Obj *key; + lucy_Obj *val; + + if (Lucy_Hash_Next(self, &key, &val)) { + SV *key_sv = (SV*)Lucy_Obj_To_Host(key); + SV *val_sv = (SV*)Lucy_Obj_To_Host(val); + + XPUSHs(sv_2mortal(key_sv)); + XPUSHs(sv_2mortal(val_sv)); + XSRETURN(2); + } + else { + XSRETURN_EMPTY; + } +} +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::Hash", + ); + $binding->exclude_method($_) for @hand_rolled; + $binding->append_xs($xs_code); + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_host { + my $xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::Host + +=for comment + +These are all for testing purposes only. + +=cut + +IV +_test(...) +CODE: + RETVAL = items; +OUTPUT: RETVAL + +SV* +_test_obj(...) +CODE: +{ + lucy_ByteBuf *test_obj = lucy_BB_new_bytes("blah", 4); + SV *pack_var = get_sv("Clownfish::Host::testobj", 1); + RETVAL = (SV*)Lucy_BB_To_Host(test_obj); + SvSetSV_nosteal(pack_var, RETVAL); + CFISH_DECREF(test_obj); + CHY_UNUSED_VAR(items); +} +OUTPUT: RETVAL + +void +_callback(obj) + lucy_Obj *obj; +PPCODE: +{ + lucy_ZombieCharBuf *blank = CFISH_ZCB_BLANK(); + lucy_Host_callback(obj, "_test", 2, + CFISH_ARG_OBJ("nothing", (lucy_CharBuf*)blank), + CFISH_ARG_I32("foo", 3)); +} + +int64_t +_callback_i64(obj) + lucy_Obj *obj; +CODE: +{ + lucy_ZombieCharBuf *blank = CFISH_ZCB_BLANK(); + RETVAL + = lucy_Host_callback_i64(obj, "_test", 2, + CFISH_ARG_OBJ("nothing", (lucy_CharBuf*)blank), + CFISH_ARG_I32("foo", 3)); +} +OUTPUT: RETVAL + +double +_callback_f64(obj) + lucy_Obj *obj; +CODE: +{ + lucy_ZombieCharBuf *blank = CFISH_ZCB_BLANK(); + RETVAL + = lucy_Host_callback_f64(obj, "_test", 2, + CFISH_ARG_OBJ("nothing", (lucy_CharBuf*)blank), + CFISH_ARG_I32("foo", 3)); +} +OUTPUT: RETVAL + +SV* +_callback_obj(obj) + lucy_Obj *obj; +CODE: +{ + lucy_Obj *other = lucy_Host_callback_obj(obj, "_test_obj", 0); + RETVAL = (SV*)Lucy_Obj_To_Host(other); + CFISH_DECREF(other); +} +OUTPUT: RETVAL +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::Host", + ); + $binding->append_xs($xs_code); + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_i32array { + my $xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Lucy::Object::I32Array + +SV* +new(either_sv, ...) + SV *either_sv; +CODE: +{ + SV *ints_sv = NULL; + lucy_I32Array *self = NULL; + + chy_bool_t args_ok + = XSBind_allot_params(&(ST(0)), 1, items, + ALLOT_SV(&ints_sv, "ints", 4, true), + NULL); + if (!args_ok) { + CFISH_RETHROW(CFISH_INCREF(cfish_Err_get_error())); + } + + AV *ints_av = NULL; + if (SvROK(ints_sv)) { + ints_av = (AV*)SvRV(ints_sv); + } + if (ints_av && SvTYPE(ints_av) == SVt_PVAV) { + int32_t size = av_len(ints_av) + 1; + int32_t *ints = (int32_t*)LUCY_MALLOCATE(size * sizeof(int32_t)); + int32_t i; + + for (i = 0; i < size; i++) { + SV **const sv_ptr = av_fetch(ints_av, i, 0); + ints[i] = (sv_ptr && XSBind_sv_defined(*sv_ptr)) + ? SvIV(*sv_ptr) + : 0; + } + self = (lucy_I32Array*)XSBind_new_blank_obj(either_sv); + lucy_I32Arr_init(self, ints, size); + } + else { + THROW(LUCY_ERR, "Required param 'ints' isn't an arrayref"); + } + + RETVAL = CFISH_OBJ_TO_SV_NOINC(self); +} +OUTPUT: RETVAL + +SV* +to_arrayref(self) + lucy_I32Array *self; +CODE: +{ + AV *out_av = newAV(); + uint32_t i; + uint32_t size = Lucy_I32Arr_Get_Size(self); + + av_extend(out_av, size); + for (i = 0; i < size; i++) { + int32_t result = Lucy_I32Arr_Get(self, i); + SV* result_sv = result == -1 ? newSV(0) : newSViv(result); + av_push(out_av, result_sv); + } + RETVAL = newRV_noinc((SV*)out_av); +} +OUTPUT: RETVAL +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Lucy::Object::I32Array", + ); + $binding->append_xs($xs_code); + $binding->exclude_constructor; + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_lockfreeregistry { + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::LockFreeRegistry", + ); + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_float32 { + my $float32_xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::Float32 + +SV* +new(either_sv, value) + SV *either_sv; + float value; +CODE: +{ + lucy_Float32 *self = (lucy_Float32*)XSBind_new_blank_obj(either_sv); + lucy_Float32_init(self, value); + RETVAL = CFISH_OBJ_TO_SV_NOINC(self); +} +OUTPUT: RETVAL +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::Float32", + ); + $binding->append_xs($float32_xs_code); + $binding->exclude_constructor; + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_float64 { + my $float64_xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::Float64 + +SV* +new(either_sv, value) + SV *either_sv; + double value; +CODE: +{ + lucy_Float64 *self = (lucy_Float64*)XSBind_new_blank_obj(either_sv); + lucy_Float64_init(self, value); + RETVAL = CFISH_OBJ_TO_SV_NOINC(self); +} +OUTPUT: RETVAL +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::Float64", + ); + $binding->append_xs($float64_xs_code); + $binding->exclude_constructor; + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_obj { + my @exposed = qw( + To_String + To_I64 + To_F64 + Equals + Dump + Load + ); + my @hand_rolled = qw( + Is_A + ); + + my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; + my $synopsis = <<'END_SYNOPSIS'; + package MyObj; + use base qw( Clownfish::Obj ); + + # Inside-out member var. + my %foo; + + sub new { + my ( $class, %args ) = @_; + my $foo = delete $args{foo}; + my $self = $class->SUPER::new(%args); + $foo{$$self} = $foo; + return $self; + } + + sub get_foo { + my $self = shift; + return $foo{$$self}; + } + + sub DESTROY { + my $self = shift; + delete $foo{$$self}; + $self->SUPER::DESTROY; + } +END_SYNOPSIS + my $description = <<'END_DESCRIPTION'; +Clownfish::Obj is the base class of the Clownfish object hierarchy. + +From the standpoint of a Perl programmer, all classes are implemented as +blessed scalar references, with the scalar storing a pointer to a C struct. + +=head2 Subclassing + +The recommended way to subclass Clownfish::Obj and its descendants is +to use the inside-out design pattern. (See L for an +introduction to inside-out techniques.) + +Since the blessed scalar stores a C pointer value which is unique per-object, +C<$$self> can be used as an inside-out ID. + + # Accessor for 'foo' member variable. + sub get_foo { + my $self = shift; + return $foo{$$self}; + } + + +Caveats: + +=over + +=item * + +Inside-out aficionados will have noted that the "cached scalar id" stratagem +recommended above isn't compatible with ithreads. + +=item * + +Overridden methods must not return undef unless the API specifies that +returning undef is permissible. (Failure to adhere to this rule currently +results in a segfault rather than an exception.) + +=back + +=head1 CONSTRUCTOR + +=head2 new() + +Abstract constructor -- must be invoked via a subclass. Attempting to +instantiate objects of class "Clownfish::Obj" directly causes an +error. + +Takes no arguments; if any are supplied, an error will be reported. + +=head1 DESTRUCTOR + +=head2 DESTROY + +All Clownfish classes implement a DESTROY method; if you override it in a +subclass, you must call C<< $self->SUPER::DESTROY >> to avoid leaking memory. +END_DESCRIPTION + $pod_spec->set_synopsis($synopsis); + $pod_spec->set_description($description); + $pod_spec->add_method( method => $_, alias => lc($_) ) for @exposed; + + my $xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::Obj + +chy_bool_t +is_a(self, class_name) + lucy_Obj *self; + const lucy_CharBuf *class_name; +CODE: +{ + lucy_VTable *target = lucy_VTable_fetch_vtable(class_name); + RETVAL = Lucy_Obj_Is_A(self, target); +} +OUTPUT: RETVAL + +void +STORABLE_freeze(self, ...) + lucy_Obj *self; +PPCODE: +{ + CHY_UNUSED_VAR(self); + if (items < 2 || !SvTRUE(ST(1))) { + SV *retval; + lucy_ByteBuf *serialized_bb; + lucy_RAMFileHandle *file_handle + = lucy_RAMFH_open(NULL, LUCY_FH_WRITE_ONLY | LUCY_FH_CREATE, NULL); + lucy_OutStream *target = lucy_OutStream_open((lucy_Obj*)file_handle); + + Lucy_Obj_Serialize(self, target); + + Lucy_OutStream_Close(target); + serialized_bb + = Lucy_RAMFile_Get_Contents(Lucy_RAMFH_Get_File(file_handle)); + retval = XSBind_bb_to_sv(serialized_bb); + CFISH_DECREF(file_handle); + CFISH_DECREF(target); + + if (SvCUR(retval) == 0) { // Thwart Storable bug + THROW(LUCY_ERR, "Calling serialize produced an empty string"); + } + ST(0) = sv_2mortal(retval); + XSRETURN(1); + } +} + +=begin comment + +Calls deserialize(), and copies the object pointer. Since deserialize is an +abstract method, it will confess() unless implemented. + +=end comment + +=cut + +void +STORABLE_thaw(blank_obj, cloning, serialized_sv) + SV *blank_obj; + SV *cloning; + SV *serialized_sv; +PPCODE: +{ + char *class_name = HvNAME(SvSTASH(SvRV(blank_obj))); + lucy_ZombieCharBuf *klass + = CFISH_ZCB_WRAP_STR(class_name, strlen(class_name)); + lucy_VTable *vtable + = (lucy_VTable*)lucy_VTable_singleton((lucy_CharBuf*)klass, NULL); + STRLEN len; + char *ptr = SvPV(serialized_sv, len); + lucy_ViewByteBuf *contents = lucy_ViewBB_new(ptr, len); + lucy_RAMFile *ram_file = lucy_RAMFile_new((lucy_ByteBuf*)contents, true); + lucy_RAMFileHandle *file_handle + = lucy_RAMFH_open(NULL, LUCY_FH_READ_ONLY, ram_file); + lucy_InStream *instream = lucy_InStream_open((lucy_Obj*)file_handle); + lucy_Obj *self = Lucy_VTable_Foster_Obj(vtable, blank_obj); + lucy_Obj *deserialized = Lucy_Obj_Deserialize(self, instream); + + CHY_UNUSED_VAR(cloning); + CFISH_DECREF(contents); + CFISH_DECREF(ram_file); + CFISH_DECREF(file_handle); + CFISH_DECREF(instream); + + // Catch bad deserialize() override. + if (deserialized != self) { + THROW(LUCY_ERR, "Error when deserializing obj of class %o", klass); + } +} +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::Obj", + ); + $binding->bind_method( alias => '_load', method => 'Load' ); + $binding->exclude_method($_) for @hand_rolled; + $binding->append_xs($xs_code); + $binding->set_pod_spec($pod_spec); + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_varray { + my @hand_rolled = qw( + Shallow_Copy + Shift + Pop + Delete + Store + Fetch + ); + + my $xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::VArray + +SV* +shallow_copy(self) + lucy_VArray *self; +CODE: + RETVAL = CFISH_OBJ_TO_SV_NOINC(Lucy_VA_Shallow_Copy(self)); +OUTPUT: RETVAL + +SV* +_deserialize(self, instream) + lucy_VArray *self; + lucy_InStream *instream; +CODE: + lucy_VArray *thawed = Lucy_VA_Deserialize(self, instream); + RETVAL = (SV*)Lucy_VA_To_Host(thawed); +OUTPUT: RETVAL + +SV* +_clone(self) + lucy_VArray *self; +CODE: + RETVAL = CFISH_OBJ_TO_SV_NOINC(Lucy_VA_Clone(self)); +OUTPUT: RETVAL + +SV* +shift(self) + lucy_VArray *self; +CODE: + RETVAL = CFISH_OBJ_TO_SV_NOINC(Lucy_VA_Shift(self)); +OUTPUT: RETVAL + +SV* +pop(self) + lucy_VArray *self; +CODE: + RETVAL = CFISH_OBJ_TO_SV_NOINC(Lucy_VA_Pop(self)); +OUTPUT: RETVAL + +SV* +delete(self, tick) + lucy_VArray *self; + uint32_t tick; +CODE: + RETVAL = CFISH_OBJ_TO_SV_NOINC(Lucy_VA_Delete(self, tick)); +OUTPUT: RETVAL + +void +store(self, tick, value); + lucy_VArray *self; + uint32_t tick; + lucy_Obj *value; +PPCODE: +{ + if (value) { CFISH_INCREF(value); } + lucy_VA_store(self, tick, value); +} + +SV* +fetch(self, tick) + lucy_VArray *self; + uint32_t tick; +CODE: + RETVAL = CFISH_OBJ_TO_SV(Lucy_VA_Fetch(self, tick)); +OUTPUT: RETVAL +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::VArray", + ); + $binding->exclude_method($_) for @hand_rolled; + $binding->append_xs($xs_code); + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +sub bind_vtable { + my @hand_rolled = qw( Make_Obj ); + + my $xs_code = <<'END_XS_CODE'; +MODULE = Lucy PACKAGE = Clownfish::VTable + +SV* +_get_registry() +CODE: + if (lucy_VTable_registry == NULL) { + lucy_VTable_init_registry(); + } + RETVAL = (SV*)Lucy_Obj_To_Host((lucy_Obj*)lucy_VTable_registry); +OUTPUT: RETVAL + +SV* +singleton(unused_sv, ...) + SV *unused_sv; +CODE: +{ + CHY_UNUSED_VAR(unused_sv); + lucy_CharBuf *class_name = NULL; + lucy_VTable *parent = NULL; + chy_bool_t args_ok + = XSBind_allot_params(&(ST(0)), 1, items, + ALLOT_OBJ(&class_name, "class_name", 10, true, + LUCY_CHARBUF, alloca(cfish_ZCB_size())), + ALLOT_OBJ(&parent, "parent", 6, false, + LUCY_VTABLE, NULL), + NULL); + if (!args_ok) { + CFISH_RETHROW(CFISH_INCREF(cfish_Err_get_error())); + } + lucy_VTable *singleton = lucy_VTable_singleton(class_name, parent); + RETVAL = (SV*)Lucy_VTable_To_Host(singleton); +} +OUTPUT: RETVAL + +SV* +make_obj(self) + lucy_VTable *self; +CODE: + lucy_Obj *blank = Lucy_VTable_Make_Obj(self); + RETVAL = CFISH_OBJ_TO_SV_NOINC(blank); +OUTPUT: RETVAL +END_XS_CODE + + my $binding = Clownfish::CFC::Binding::Perl::Class->new( + parcel => "Lucy", + class_name => "Clownfish::VTable", + ); + $binding->exclude_method($_) for @hand_rolled; + $binding->append_xs($xs_code); + + Clownfish::CFC::Binding::Perl::Class->register($binding); +} + +1; diff --git a/clownfish/perl/lib/Clownfish.pm b/clownfish/perl/lib/Clownfish.pm new file mode 100644 index 000000000..e8b222ca6 --- /dev/null +++ b/clownfish/perl/lib/Clownfish.pm @@ -0,0 +1,502 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; + +package Lucy; + +use 5.008003; +use Exporter; + +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +# On most UNIX variants, this flag makes DynaLoader pass RTLD_GLOBAL to +# dl_open, so extensions can resolve the needed symbols without explicitly +# linking against the DSO. +sub dl_load_flags { 1 } + +BEGIN { + require DynaLoader; + our @ISA = qw( DynaLoader Exporter ); + # This loads a large number of disparate subs. + bootstrap Lucy '0.3.0'; + _init_autobindings(); + our @EXPORT_OK = qw( to_clownfish to_perl kdump ); +} + +sub kdump { + require Data::Dumper; + my $kdumper = Data::Dumper->new( [@_] ); + $kdumper->Sortkeys( sub { return [ sort keys %{ $_[0] } ] } ); + $kdumper->Indent(1); + warn $kdumper->Dump; +} + +sub error {$Clownfish::Err::error} + +{ + package Lucy::Util::IndexFileNames; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + BEGIN { + push our @ISA, 'Exporter'; + our @EXPORT_OK = qw( + extract_gen + latest_snapshot + ); + } +} + +{ + package Clownfish::Util::StringHelper; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + BEGIN { + push our @ISA, 'Exporter'; + our @EXPORT_OK = qw( + utf8_flag_on + utf8_flag_off + to_base36 + from_base36 + utf8ify + utf8_valid + cat_bytes + ); + } +} + +{ + # Temporary back compat. + package Lucy::Object::Obj; + BEGIN { our @ISA = qw( Clownfish::Obj ) } +} + +{ + package Lucy::Analysis::RegexTokenizer; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + + sub compile_token_re { return qr/$_[1]/ } + + sub new { + my ( $either, %args ) = @_; + my $token_re = delete $args{token_re}; + $args{pattern} = "$token_re" if $token_re; + return $either->_new(%args); + } +} + +{ + package Lucy::Document::Doc; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use Storable qw( nfreeze thaw ); + use bytes; + no bytes; + + use overload + fallback => 1, + '%{}' => \&get_fields; + + sub serialize_fields { + my ( $self, $outstream ) = @_; + my $buf = nfreeze( $self->get_fields ); + $outstream->write_c32( bytes::length($buf) ); + $outstream->print($buf); + } + + sub deserialize_fields { + my ( $self, $instream ) = @_; + my $len = $instream->read_c32; + my $buf; + $instream->read( $buf, $len ); + $self->set_fields( thaw($buf) ); + } +} + +{ + package Clownfish::LockFreeRegistry; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + no warnings 'redefine'; + sub DESTROY { } # leak all +} + +{ + package Clownfish::Obj; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use Lucy qw( to_clownfish to_perl ); + sub load { return $_[0]->_load( to_clownfish( $_[1] ) ) } +} + +{ + package Clownfish::VTable; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + + sub find_parent_class { + my ( undef, $package ) = @_; + no strict 'refs'; + for my $parent ( @{"$package\::ISA"} ) { + return $parent if $parent->isa('Clownfish::Obj'); + } + return; + } + + sub fresh_host_methods { + my ( undef, $package ) = @_; + no strict 'refs'; + my $stash = \%{"$package\::"}; + my $methods + = Clownfish::VArray->new( capacity => scalar keys %$stash ); + while ( my ( $symbol, $glob ) = each %$stash ) { + next if ref $glob; + next unless *$glob{CODE}; + $methods->push( Clownfish::CharBuf->new($symbol) ); + } + return $methods; + } + + sub _register { + my ( undef, %args ) = @_; + my $singleton_class = $args{singleton}->get_name; + my $parent_class = $args{parent}->get_name; + if ( !$singleton_class->isa($parent_class) ) { + no strict 'refs'; + push @{"$singleton_class\::ISA"}, $parent_class; + } + } + + no warnings 'redefine'; + sub DESTROY { } # leak all +} + +{ + package Lucy::Index::Indexer; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + + sub new { + my ( $either, %args ) = @_; + my $flags = 0; + $flags |= CREATE if delete $args{'create'}; + $flags |= TRUNCATE if delete $args{'truncate'}; + return $either->_new( %args, flags => $flags ); + } +} + +{ + package Lucy::Index::IndexReader; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use Carp; + + sub new { + confess( + "IndexReader is an abstract class; use open() instead of new()"); + } + sub lexicon { + my $self = shift; + my $lex_reader = $self->fetch("Lucy::Index::LexiconReader"); + return $lex_reader->lexicon(@_) if $lex_reader; + return; + } + sub posting_list { + my $self = shift; + my $plist_reader = $self->fetch("Lucy::Index::PostingListReader"); + return $plist_reader->posting_list(@_) if $plist_reader; + return; + } + sub offsets { shift->_offsets->to_arrayref } +} + +{ + package Lucy::Index::Segment; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use Lucy qw( to_clownfish ); + sub store_metadata { + my ( $self, %args ) = @_; + $self->_store_metadata( %args, + metadata => to_clownfish( $args{metadata} ) ); + } +} + +{ + package Lucy::Search::Compiler; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use Carp; + use Scalar::Util qw( blessed ); + + sub new { + my ( $either, %args ) = @_; + if ( !defined $args{boost} ) { + confess("'parent' is not a Query") + unless ( blessed( $args{parent} ) + and $args{parent}->isa("Lucy::Search::Query") ); + $args{boost} = $args{parent}->get_boost; + } + return $either->do_new(%args); + } +} + +{ + package Lucy::Search::Query; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + + sub make_compiler { + my ( $self, %args ) = @_; + $args{boost} = $self->get_boost unless defined $args{boost}; + return $self->_make_compiler(%args); + } +} + +{ + package Lucy::Search::SortRule; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use Carp; + + my %types = ( + field => FIELD(), + score => SCORE(), + doc_id => DOC_ID(), + ); + + sub new { + my ( $either, %args ) = @_; + my $type = delete $args{type} || 'field'; + confess("Invalid type: '$type'") unless defined $types{$type}; + return $either->_new( %args, type => $types{$type} ); + } +} + +{ + package Lucy::Object::BitVector; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + sub to_arrayref { shift->to_array->to_arrayref } +} + +{ + package Clownfish::ByteBuf; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + { + # Override autogenerated deserialize binding. + no warnings 'redefine'; + sub deserialize { shift->_deserialize(@_) } + } +} + +{ + package Clownfish::ViewByteBuf; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use Carp; + sub new { confess "ViewByteBuf objects can only be created from C." } +} + +{ + package Clownfish::CharBuf; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + + { + # Defeat obscure bugs in the XS auto-generation by redefining clone() + # and deserialize(). (Because of how the typemap works for CharBuf*, + # the auto-generated methods return UTF-8 Perl scalars rather than + # actual CharBuf objects.) + no warnings 'redefine'; + sub clone { shift->_clone(@_) } + sub deserialize { shift->_deserialize(@_) } + } +} + +{ + package Clownfish::ViewCharBuf; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use Carp; + sub new { confess "ViewCharBuf has no public constructor." } +} + +{ + package Clownfish::ZombieCharBuf; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use Carp; + sub new { confess "ZombieCharBuf objects can only be created from C." } + no warnings 'redefine'; + sub DESTROY { } +} + +{ + package Clownfish::Err; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + sub do_to_string { shift->to_string } + use Scalar::Util qw( blessed ); + use Carp qw( confess longmess ); + use overload + '""' => \&do_to_string, + fallback => 1; + + sub new { + my ( $either, $message ) = @_; + my ( undef, $file, $line ) = caller; + $message .= ", $file line $line\n"; + return $either->_new( mess => Clownfish::CharBuf->new($message) ); + } + + sub do_throw { + my $err = shift; + my $longmess = longmess(); + $longmess =~ s/^\s*/\t/; + $err->cat_mess($longmess); + die $err; + } + + our $error; + sub set_error { + my $val = $_[1]; + if ( defined $val ) { + confess("Not a Clownfish::Err") + unless ( blessed($val) + && $val->isa("Clownfish::Err") ); + } + $error = $val; + } + sub get_error {$error} +} + +{ + package Clownfish::Hash; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + no warnings 'redefine'; + sub deserialize { shift->_deserialize(@_) } +} + +{ + package Clownfish::VArray; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + no warnings 'redefine'; + sub clone { CORE::shift->_clone } + sub deserialize { CORE::shift->_deserialize(@_) } +} + +{ + package Lucy::Store::FileHandle; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + BEGIN { + push our @ISA, 'Exporter'; + our @EXPORT_OK = qw( build_fh_flags ); + } + + sub build_fh_flags { + my $args = shift; + my $flags = 0; + $flags |= FH_CREATE if delete $args->{create}; + $flags |= FH_READ_ONLY if delete $args->{read_only}; + $flags |= FH_WRITE_ONLY if delete $args->{write_only}; + $flags |= FH_EXCLUSIVE if delete $args->{exclusive}; + return $flags; + } + + sub open { + my ( $either, %args ) = @_; + $args{flags} ||= 0; + $args{flags} |= build_fh_flags( \%args ); + return $either->_open(%args); + } +} + +{ + package Lucy::Store::FSFileHandle; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + + sub open { + my ( $either, %args ) = @_; + $args{flags} ||= 0; + $args{flags} |= Lucy::Store::FileHandle::build_fh_flags( \%args ); + return $either->_open(%args); + } +} + +{ + package Lucy::Store::FSFolder; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + use File::Spec::Functions qw( rel2abs ); + sub absolutify { return rel2abs( $_[1] ) } +} + +{ + package Lucy::Store::RAMFileHandle; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + + sub open { + my ( $either, %args ) = @_; + $args{flags} ||= 0; + $args{flags} |= Lucy::Store::FileHandle::build_fh_flags( \%args ); + return $either->_open(%args); + } +} + +{ + package Lucy::Util::Debug; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + BEGIN { + push our @ISA, 'Exporter'; + our @EXPORT_OK = qw( + DEBUG + DEBUG_PRINT + DEBUG_ENABLED + ASSERT + set_env_cache + num_allocated + num_freed + num_globals + ); + } +} + +{ + package Clownfish::Host; + our $VERSION = '0.003000'; + $VERSION = eval $VERSION; + BEGIN { + if ( !__PACKAGE__->isa('Clownfish::Obj') ) { + push our @ISA, 'Clownfish::Obj'; + } + } +} + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/ByteBuf.pm b/clownfish/perl/lib/Clownfish/ByteBuf.pm new file mode 100644 index 000000000..34698c7eb --- /dev/null +++ b/clownfish/perl/lib/Clownfish/ByteBuf.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::ByteBuf; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/CharBuf.pm b/clownfish/perl/lib/Clownfish/CharBuf.pm new file mode 100644 index 000000000..c666613de --- /dev/null +++ b/clownfish/perl/lib/Clownfish/CharBuf.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::CharBuf; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/Err.pm b/clownfish/perl/lib/Clownfish/Err.pm new file mode 100644 index 000000000..68c911889 --- /dev/null +++ b/clownfish/perl/lib/Clownfish/Err.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::Err; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/Hash.pm b/clownfish/perl/lib/Clownfish/Hash.pm new file mode 100644 index 000000000..0dbcd841d --- /dev/null +++ b/clownfish/perl/lib/Clownfish/Hash.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::Hash; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/Host.pm b/clownfish/perl/lib/Clownfish/Host.pm new file mode 100644 index 000000000..2a76ba815 --- /dev/null +++ b/clownfish/perl/lib/Clownfish/Host.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::Host; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/LockFreeRegistry.pm b/clownfish/perl/lib/Clownfish/LockFreeRegistry.pm new file mode 100644 index 000000000..b2d8ea56f --- /dev/null +++ b/clownfish/perl/lib/Clownfish/LockFreeRegistry.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::LockFreeRegistry; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/Num.pm b/clownfish/perl/lib/Clownfish/Num.pm new file mode 100644 index 000000000..fd63caa89 --- /dev/null +++ b/clownfish/perl/lib/Clownfish/Num.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::Num; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/Obj.pm b/clownfish/perl/lib/Clownfish/Obj.pm new file mode 100644 index 000000000..d7208c7d1 --- /dev/null +++ b/clownfish/perl/lib/Clownfish/Obj.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::Obj; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/VArray.pm b/clownfish/perl/lib/Clownfish/VArray.pm new file mode 100644 index 000000000..411cc4ba0 --- /dev/null +++ b/clownfish/perl/lib/Clownfish/VArray.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::VArray; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/lib/Clownfish/VTable.pm b/clownfish/perl/lib/Clownfish/VTable.pm new file mode 100644 index 000000000..00fb050cd --- /dev/null +++ b/clownfish/perl/lib/Clownfish/VTable.pm @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package Clownfish::VTable; +use Lucy; +our $VERSION = '0.003000'; +$VERSION = eval $VERSION; + +1; + +__END__ + + diff --git a/clownfish/perl/xs/Clownfish/Err.c b/clownfish/perl/xs/Clownfish/Err.c new file mode 100644 index 000000000..763178d29 --- /dev/null +++ b/clownfish/perl/xs/Clownfish/Err.c @@ -0,0 +1,146 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "XSBind.h" +#include "Clownfish/Host.h" + +// Anonymous XSUB helper for Err#trap(). It wraps the supplied C function +// so that it can be run inside a Perl eval block. +static SV *attempt_xsub = NULL; + +XS(lucy_Err_attempt_via_xs) { + dXSARGS; + CHY_UNUSED_VAR(cv); + SP -= items; + if (items != 2) { + CFISH_THROW(CFISH_ERR, "Usage: $sub->(routine, context)"); + }; + IV routine_iv = SvIV(ST(0)); + IV context_iv = SvIV(ST(1)); + Cfish_Err_Attempt_t routine = INT2PTR(Cfish_Err_Attempt_t, routine_iv); + void *context = INT2PTR(void*, context_iv); + routine(context); + XSRETURN(0); +} + +void +lucy_Err_init_class(void) { + char *file = (char*)__FILE__; + attempt_xsub = (SV*)newXS(NULL, lucy_Err_attempt_via_xs, file); +} + +lucy_Err* +lucy_Err_get_error() { + lucy_Err *error + = (lucy_Err*)lucy_Host_callback_obj(LUCY_ERR, "get_error", 0); + CFISH_DECREF(error); // Cancel out incref from callback. + return error; +} + +void +lucy_Err_set_error(lucy_Err *error) { + lucy_Host_callback(LUCY_ERR, "set_error", 1, + CFISH_ARG_OBJ("error", error)); + CFISH_DECREF(error); +} + +void +lucy_Err_do_throw(lucy_Err *err) { + dSP; + SV *error_sv = (SV*)Lucy_Err_To_Host(err); + CFISH_DECREF(err); + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(error_sv)); + PUTBACK; + call_pv("Clownfish::Err::do_throw", G_DISCARD); + FREETMPS; + LEAVE; +} + +void* +lucy_Err_to_host(lucy_Err *self) { + Lucy_Err_To_Host_t super_to_host + = CFISH_SUPER_METHOD_PTR(LUCY_ERR, Lucy_Err_To_Host); + SV *perl_obj = (SV*)super_to_host(self); + XSBind_enable_overload(perl_obj); + return perl_obj; +} + +void +lucy_Err_throw_mess(lucy_VTable *vtable, lucy_CharBuf *message) { + Lucy_Err_Make_t make + = CFISH_METHOD_PTR(CFISH_CERTIFY(vtable, LUCY_VTABLE), Lucy_Err_Make); + lucy_Err *err = (lucy_Err*)CFISH_CERTIFY(make(NULL), LUCY_ERR); + Lucy_Err_Cat_Mess(err, message); + CFISH_DECREF(message); + lucy_Err_do_throw(err); +} + +void +lucy_Err_warn_mess(lucy_CharBuf *message) { + SV *error_sv = XSBind_cb_to_sv(message); + CFISH_DECREF(message); + warn("%s", SvPV_nolen(error_sv)); + SvREFCNT_dec(error_sv); +} + +lucy_Err* +lucy_Err_trap(Cfish_Err_Attempt_t routine, void *context) { + lucy_Err *error = NULL; + SV *routine_sv = newSViv(PTR2IV(routine)); + SV *context_sv = newSViv(PTR2IV(context)); + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + EXTEND(SP, 2); + PUSHs(sv_2mortal(routine_sv)); + PUSHs(sv_2mortal(context_sv)); + PUTBACK; + + int count = call_sv(attempt_xsub, G_EVAL | G_DISCARD); + if (count != 0) { + lucy_CharBuf *mess + = lucy_CB_newf("'attempt' returned too many values: %i32", + (int32_t)count); + error = lucy_Err_new(mess); + } + else { + SV *dollar_at = get_sv("@", FALSE); + if (SvTRUE(dollar_at)) { + if (sv_isobject(dollar_at) + && sv_derived_from(dollar_at,"Clownfish::Err") + ) { + IV error_iv = SvIV(SvRV(dollar_at)); + error = INT2PTR(lucy_Err*, error_iv); + CFISH_INCREF(error); + } + else { + STRLEN len; + char *ptr = SvPVutf8(dollar_at, len); + lucy_CharBuf *mess = lucy_CB_new_from_trusted_utf8(ptr, len); + error = lucy_Err_new(mess); + } + } + } + FREETMPS; + LEAVE; + + return error; +} + diff --git a/clownfish/perl/xs/Clownfish/Host.c b/clownfish/perl/xs/Clownfish/Host.c new file mode 100644 index 000000000..8616557fe --- /dev/null +++ b/clownfish/perl/xs/Clownfish/Host.c @@ -0,0 +1,246 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "XSBind.h" + +#include "Clownfish/VTable.h" + +#include "Clownfish/Obj.h" +#include "Clownfish/Host.h" +#include "Clownfish/CharBuf.h" +#include "Clownfish/Err.h" +#include "Clownfish/Util/Memory.h" + +static SV* +S_do_callback_sv(void *vobj, const char *method, uint32_t num_args, + va_list args); + +// Convert all arguments to Perl and place them on the Perl stack. +static CHY_INLINE void +SI_push_args(void *vobj, va_list args, uint32_t num_args) { + lucy_Obj *obj = (lucy_Obj*)vobj; + SV *invoker; + dSP; + + uint32_t stack_slots_needed = num_args < 2 + ? num_args + 1 + : (num_args * 2) + 1; + EXTEND(SP, stack_slots_needed); + + if (Lucy_Obj_Is_A(obj, LUCY_VTABLE)) { + lucy_VTable *vtable = (lucy_VTable*)obj; + // TODO: Creating a new class name SV every time is wasteful. + invoker = XSBind_cb_to_sv(Lucy_VTable_Get_Name(vtable)); + } + else { + invoker = (SV*)Lucy_Obj_To_Host(obj); + } + + ENTER; + SAVETMPS; + PUSHMARK(SP); + PUSHs(sv_2mortal(invoker)); + + for (uint32_t i = 0; i < num_args; i++) { + uint32_t arg_type = va_arg(args, uint32_t); + char *label = va_arg(args, char*); + if (num_args > 1) { + PUSHs(sv_2mortal(newSVpvn(label, strlen(label)))); + } + switch (arg_type & CFISH_HOST_ARGTYPE_MASK) { + case CFISH_HOST_ARGTYPE_I32: { + int32_t value = va_arg(args, int32_t); + PUSHs(sv_2mortal(newSViv(value))); + } + break; + case CFISH_HOST_ARGTYPE_I64: { + int64_t value = va_arg(args, int64_t); + if (sizeof(IV) == 8) { + PUSHs(sv_2mortal(newSViv((IV)value))); + } + else { + // lossy + PUSHs(sv_2mortal(newSVnv((double)value))); + } + } + break; + case CFISH_HOST_ARGTYPE_F32: + case CFISH_HOST_ARGTYPE_F64: { + // Floats are promoted to doubles by variadic calling. + double value = va_arg(args, double); + PUSHs(sv_2mortal(newSVnv(value))); + } + break; + case CFISH_HOST_ARGTYPE_STR: { + lucy_CharBuf *string = va_arg(args, lucy_CharBuf*); + PUSHs(sv_2mortal(XSBind_cb_to_sv(string))); + } + break; + case CFISH_HOST_ARGTYPE_OBJ: { + lucy_Obj* anObj = va_arg(args, lucy_Obj*); + SV *arg_sv = anObj == NULL + ? newSV(0) + : XSBind_cfish_to_perl(anObj); + PUSHs(sv_2mortal(arg_sv)); + } + break; + default: + CFISH_THROW(LUCY_ERR, "Unrecognized arg type: %u32", + arg_type); + } + } + + PUTBACK; +} + +void +lucy_Host_callback(void *vobj, const char *method, uint32_t num_args, ...) { + va_list args; + + va_start(args, num_args); + SI_push_args(vobj, args, num_args); + va_end(args); + + int count = call_method(method, G_VOID | G_DISCARD); + if (count != 0) { + CFISH_THROW(LUCY_ERR, "callback '%s' returned too many values: %i32", + method, (int32_t)count); + } + FREETMPS; + LEAVE; +} + +int64_t +lucy_Host_callback_i64(void *vobj, const char *method, uint32_t num_args, ...) { + va_list args; + SV *return_sv; + int64_t retval; + + va_start(args, num_args); + return_sv = S_do_callback_sv(vobj, method, num_args, args); + va_end(args); + if (sizeof(IV) == 8) { + retval = (int64_t)SvIV(return_sv); + } + else { + if (SvIOK(return_sv)) { + // It's already no more than 32 bits, so don't convert. + retval = SvIV(return_sv); + } + else { + // Maybe lossy. + double temp = SvNV(return_sv); + retval = (int64_t)temp; + } + } + + FREETMPS; + LEAVE; + + return retval; +} + +double +lucy_Host_callback_f64(void *vobj, const char *method, uint32_t num_args, ...) { + va_list args; + SV *return_sv; + double retval; + + va_start(args, num_args); + return_sv = S_do_callback_sv(vobj, method, num_args, args); + va_end(args); + retval = SvNV(return_sv); + + FREETMPS; + LEAVE; + + return retval; +} + +lucy_Obj* +lucy_Host_callback_obj(void *vobj, const char *method, uint32_t num_args, ...) { + va_list args; + SV *temp_retval; + lucy_Obj *retval = NULL; + + va_start(args, num_args); + temp_retval = S_do_callback_sv(vobj, method, num_args, args); + va_end(args); + + retval = XSBind_perl_to_cfish(temp_retval); + + FREETMPS; + LEAVE; + + return retval; +} + +lucy_CharBuf* +lucy_Host_callback_str(void *vobj, const char *method, uint32_t num_args, ...) { + va_list args; + SV *temp_retval; + lucy_CharBuf *retval = NULL; + + va_start(args, num_args); + temp_retval = S_do_callback_sv(vobj, method, num_args, args); + va_end(args); + + // Make a stringified copy. + if (temp_retval && XSBind_sv_defined(temp_retval)) { + STRLEN len; + char *ptr = SvPVutf8(temp_retval, len); + retval = lucy_CB_new_from_trusted_utf8(ptr, len); + } + + FREETMPS; + LEAVE; + + return retval; +} + +void* +lucy_Host_callback_host(void *vobj, const char *method, uint32_t num_args, ...) { + va_list args; + SV *retval; + + va_start(args, num_args); + retval = S_do_callback_sv(vobj, method, num_args, args); + va_end(args); + SvREFCNT_inc(retval); + + FREETMPS; + LEAVE; + + return retval; +} + +static SV* +S_do_callback_sv(void *vobj, const char *method, uint32_t num_args, + va_list args) { + SV *return_val; + SI_push_args(vobj, args, num_args); + int num_returned = call_method(method, G_SCALAR); + dSP; + if (num_returned != 1) { + CFISH_THROW(LUCY_ERR, "Bad number of return vals from %s: %i32", + method, (int32_t)num_returned); + } + return_val = POPs; + PUTBACK; + return return_val; +} + + diff --git a/clownfish/perl/xs/Clownfish/LockFreeRegistry.c b/clownfish/perl/xs/Clownfish/LockFreeRegistry.c new file mode 100644 index 000000000..41f789957 --- /dev/null +++ b/clownfish/perl/xs/Clownfish/LockFreeRegistry.c @@ -0,0 +1,36 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_OBJ +#define C_LUCY_LOCKFREEREGISTRY +#include "XSBind.h" + +#include "Clownfish/LockFreeRegistry.h" +#include "Clownfish/Host.h" + +void* +lucy_LFReg_to_host(lucy_LockFreeRegistry *self) { + chy_bool_t first_time = self->ref.count < 4 ? true : false; + Lucy_LFReg_To_Host_t to_host + = CFISH_SUPER_METHOD_PTR(LUCY_LOCKFREEREGISTRY, Lucy_LFReg_To_Host); + SV *host_obj = (SV*)to_host(self); + if (first_time) { + SvSHARE((SV*)self->ref.host_obj); + } + return host_obj; +} + + diff --git a/clownfish/perl/xs/Clownfish/Obj.c b/clownfish/perl/xs/Clownfish/Obj.c new file mode 100644 index 000000000..38fcd2d30 --- /dev/null +++ b/clownfish/perl/xs/Clownfish/Obj.c @@ -0,0 +1,113 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_OBJ + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" +#include "ppport.h" + +#include "Clownfish/Obj.h" +#include "Clownfish/CharBuf.h" +#include "Clownfish/Err.h" +#include "Clownfish/VTable.h" +#include "Clownfish/Util/Memory.h" + +static void +S_lazy_init_host_obj(lucy_Obj *self) { + SV *inner_obj = newSV(0); + SvOBJECT_on(inner_obj); + PL_sv_objcount++; + SvUPGRADE(inner_obj, SVt_PVMG); + sv_setiv(inner_obj, PTR2IV(self)); + + // Connect class association. + lucy_CharBuf *class_name = Lucy_VTable_Get_Name(self->vtable); + HV *stash = gv_stashpvn((char*)Lucy_CB_Get_Ptr8(class_name), + Lucy_CB_Get_Size(class_name), TRUE); + SvSTASH_set(inner_obj, (HV*)SvREFCNT_inc(stash)); + + /* Up till now we've been keeping track of the refcount in + * self->ref.count. We're replacing ref.count with ref.host_obj, which + * will assume responsibility for maintaining the refcount. ref.host_obj + * starts off with a refcount of 1, so we need to transfer any refcounts + * in excess of that. */ + size_t old_refcount = self->ref.count; + self->ref.host_obj = inner_obj; + while (old_refcount > 1) { + SvREFCNT_inc_simple_void_NN(inner_obj); + old_refcount--; + } +} + +uint32_t +lucy_Obj_get_refcount(lucy_Obj *self) { + return self->ref.count < 4 + ? self->ref.count + : SvREFCNT((SV*)self->ref.host_obj); +} + +lucy_Obj* +lucy_Obj_inc_refcount(lucy_Obj *self) { + switch (self->ref.count) { + case 0: + CFISH_THROW(LUCY_ERR, "Illegal refcount of 0"); + break; // useless + case 1: + case 2: + self->ref.count++; + break; + case 3: + S_lazy_init_host_obj(self); + // fall through + default: + SvREFCNT_inc_simple_void_NN((SV*)self->ref.host_obj); + } + return self; +} + +uint32_t +lucy_Obj_dec_refcount(lucy_Obj *self) { + uint32_t modified_refcount = I32_MAX; + switch (self->ref.count) { + case 0: + CFISH_THROW(LUCY_ERR, "Illegal refcount of 0"); + break; // useless + case 1: + modified_refcount = 0; + Lucy_Obj_Destroy(self); + break; + case 2: + case 3: + modified_refcount = --self->ref.count; + break; + default: + modified_refcount = SvREFCNT((SV*)self->ref.host_obj) - 1; + // If the SV's refcount falls to 0, DESTROY will be invoked from + // Perl-space. + SvREFCNT_dec((SV*)self->ref.host_obj); + } + return modified_refcount; +} + +void* +lucy_Obj_to_host(lucy_Obj *self) { + if (self->ref.count < 4) { S_lazy_init_host_obj(self); } + return newRV_inc((SV*)self->ref.host_obj); +} + + diff --git a/clownfish/perl/xs/Clownfish/VTable.c b/clownfish/perl/xs/Clownfish/VTable.c new file mode 100644 index 000000000..52879248e --- /dev/null +++ b/clownfish/perl/xs/Clownfish/VTable.c @@ -0,0 +1,70 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_OBJ +#define C_LUCY_VTABLE +#include "XSBind.h" + +#include "Clownfish/VTable.h" +#include "Clownfish/Host.h" +#include "Clownfish/Util/Memory.h" + +lucy_Obj* +lucy_VTable_foster_obj(lucy_VTable *self, void *host_obj) { + lucy_Obj *obj + = (lucy_Obj*)lucy_Memory_wrapped_calloc(self->obj_alloc_size, 1); + SV *inner_obj = SvRV((SV*)host_obj); + obj->vtable = self; + sv_setiv(inner_obj, PTR2IV(obj)); + obj->ref.host_obj = inner_obj; + return obj; +} + +void +lucy_VTable_register_with_host(lucy_VTable *singleton, lucy_VTable *parent) { + // Register class with host. + lucy_Host_callback(LUCY_VTABLE, "_register", 2, + CFISH_ARG_OBJ("singleton", singleton), + CFISH_ARG_OBJ("parent", parent)); +} + +lucy_VArray* +lucy_VTable_fresh_host_methods(const lucy_CharBuf *class_name) { + return (lucy_VArray*)lucy_Host_callback_obj( + LUCY_VTABLE, + "fresh_host_methods", 1, + CFISH_ARG_STR("class_name", class_name)); +} + +lucy_CharBuf* +lucy_VTable_find_parent_class(const lucy_CharBuf *class_name) { + return lucy_Host_callback_str(LUCY_VTABLE, "find_parent_class", 1, + CFISH_ARG_STR("class_name", class_name)); +} + +void* +lucy_VTable_to_host(lucy_VTable *self) { + chy_bool_t first_time = self->ref.count < 4 ? true : false; + Lucy_VTable_To_Host_t to_host + = CFISH_SUPER_METHOD_PTR(LUCY_VTABLE, Lucy_VTable_To_Host); + SV *host_obj = (SV*)to_host(self); + if (first_time) { + SvSHARE((SV*)self->ref.host_obj); + } + return host_obj; +} + + diff --git a/clownfish/perl/xs/XSBind.c b/clownfish/perl/xs/XSBind.c new file mode 100644 index 000000000..ee28681f2 --- /dev/null +++ b/clownfish/perl/xs/XSBind.c @@ -0,0 +1,582 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define C_LUCY_OBJ +#define NEED_newRV_noinc +#include "XSBind.h" +#include "Clownfish/Util/StringHelper.h" +#include "Clownfish/Util/NumberUtils.h" + +// Convert a Perl hash into a Clownfish Hash. Caller takes responsibility for +// a refcount. +static cfish_Hash* +S_perl_hash_to_cfish_hash(HV *phash); + +// Convert a Perl array into a Clownfish VArray. Caller takes responsibility +// for a refcount. +static cfish_VArray* +S_perl_array_to_cfish_array(AV *parray); + +// Convert a VArray to a Perl array. Caller takes responsibility for a +// refcount. +static SV* +S_cfish_array_to_perl_array(cfish_VArray *varray); + +// Convert a Hash to a Perl hash. Caller takes responsibility for a refcount. +static SV* +S_cfish_hash_to_perl_hash(cfish_Hash *hash); + +cfish_Obj* +XSBind_new_blank_obj(SV *either_sv) { + cfish_VTable *vtable; + + // Get a VTable. + if (sv_isobject(either_sv) + && sv_derived_from(either_sv, "Clownfish::Obj") + ) { + // Use the supplied object's VTable. + IV iv_ptr = SvIV(SvRV(either_sv)); + cfish_Obj *self = INT2PTR(cfish_Obj*, iv_ptr); + vtable = self->vtable; + } + else { + // Use the supplied class name string to find a VTable. + STRLEN len; + char *ptr = SvPVutf8(either_sv, len); + cfish_ZombieCharBuf *klass = CFISH_ZCB_WRAP_STR(ptr, len); + vtable = cfish_VTable_singleton((cfish_CharBuf*)klass, NULL); + } + + // Use the VTable to allocate a new blank object of the right size. + return Cfish_VTable_Make_Obj(vtable); +} + +cfish_Obj* +XSBind_sv_to_cfish_obj(SV *sv, cfish_VTable *vtable, void *allocation) { + cfish_Obj *retval = XSBind_maybe_sv_to_cfish_obj(sv, vtable, allocation); + if (!retval) { + THROW(CFISH_ERR, "Not a %o", Cfish_VTable_Get_Name(vtable)); + } + return retval; +} + +cfish_Obj* +XSBind_maybe_sv_to_cfish_obj(SV *sv, cfish_VTable *vtable, void *allocation) { + cfish_Obj *retval = NULL; + if (XSBind_sv_defined(sv)) { + if (sv_isobject(sv) + && sv_derived_from(sv, (char*)Cfish_CB_Get_Ptr8(Cfish_VTable_Get_Name(vtable))) + ) { + // Unwrap a real Clownfish object. + IV tmp = SvIV(SvRV(sv)); + retval = INT2PTR(cfish_Obj*, tmp); + } + else if (allocation && + (vtable == CFISH_ZOMBIECHARBUF + || vtable == CFISH_VIEWCHARBUF + || vtable == CFISH_CHARBUF + || vtable == CFISH_OBJ) + ) { + // Wrap the string from an ordinary Perl scalar inside a + // ZombieCharBuf. + STRLEN size; + char *ptr = SvPVutf8(sv, size); + retval = (cfish_Obj*)cfish_ZCB_wrap_str(allocation, ptr, size); + } + else if (SvROK(sv)) { + // Attempt to convert Perl hashes and arrays into their Clownfish + // analogues. + SV *inner = SvRV(sv); + if (SvTYPE(inner) == SVt_PVAV && vtable == CFISH_VARRAY) { + retval = (cfish_Obj*)S_perl_array_to_cfish_array((AV*)inner); + } + else if (SvTYPE(inner) == SVt_PVHV && vtable == CFISH_HASH) { + retval = (cfish_Obj*)S_perl_hash_to_cfish_hash((HV*)inner); + } + + if (retval) { + // Mortalize the converted object -- which is somewhat + // dangerous, but is the only way to avoid requiring that the + // caller take responsibility for a refcount. + SV *mortal = (SV*)Cfish_Obj_To_Host(retval); + CFISH_DECREF(retval); + sv_2mortal(mortal); + } + } + } + + return retval; +} + +SV* +XSBind_cfish_to_perl(cfish_Obj *obj) { + if (obj == NULL) { + return newSV(0); + } + else if (Cfish_Obj_Is_A(obj, CFISH_CHARBUF)) { + return XSBind_cb_to_sv((cfish_CharBuf*)obj); + } + else if (Cfish_Obj_Is_A(obj, CFISH_BYTEBUF)) { + return XSBind_bb_to_sv((cfish_ByteBuf*)obj); + } + else if (Cfish_Obj_Is_A(obj, CFISH_VARRAY)) { + return S_cfish_array_to_perl_array((cfish_VArray*)obj); + } + else if (Cfish_Obj_Is_A(obj, CFISH_HASH)) { + return S_cfish_hash_to_perl_hash((cfish_Hash*)obj); + } + else if (Cfish_Obj_Is_A(obj, CFISH_FLOATNUM)) { + return newSVnv(Cfish_Obj_To_F64(obj)); + } + else if (obj == (cfish_Obj*)CFISH_TRUE) { + return newSViv(1); + } + else if (obj == (cfish_Obj*)CFISH_FALSE) { + return newSViv(0); + } + else if (sizeof(IV) == 8 && Cfish_Obj_Is_A(obj, CFISH_INTNUM)) { + int64_t num = Cfish_Obj_To_I64(obj); + return newSViv((IV)num); + } + else if (sizeof(IV) == 4 && Cfish_Obj_Is_A(obj, CFISH_INTEGER32)) { + int32_t num = (int32_t)Cfish_Obj_To_I64(obj); + return newSViv((IV)num); + } + else if (sizeof(IV) == 4 && Cfish_Obj_Is_A(obj, CFISH_INTEGER64)) { + int64_t num = Cfish_Obj_To_I64(obj); + return newSVnv((double)num); // lossy + } + else { + return (SV*)Cfish_Obj_To_Host(obj); + } +} + +cfish_Obj* +XSBind_perl_to_cfish(SV *sv) { + cfish_Obj *retval = NULL; + + if (XSBind_sv_defined(sv)) { + if (SvROK(sv)) { + // Deep conversion of references. + SV *inner = SvRV(sv); + if (SvTYPE(inner) == SVt_PVAV) { + retval = (cfish_Obj*)S_perl_array_to_cfish_array((AV*)inner); + } + else if (SvTYPE(inner) == SVt_PVHV) { + retval = (cfish_Obj*)S_perl_hash_to_cfish_hash((HV*)inner); + } + else if (sv_isobject(sv) + && sv_derived_from(sv, "Clownfish::Obj") + ) { + IV tmp = SvIV(inner); + retval = INT2PTR(cfish_Obj*, tmp); + (void)CFISH_INCREF(retval); + } + } + + // It's either a plain scalar or a non-Clownfish Perl object, so + // stringify. + if (!retval) { + STRLEN len; + char *ptr = SvPVutf8(sv, len); + retval = (cfish_Obj*)cfish_CB_new_from_trusted_utf8(ptr, len); + } + } + else if (sv) { + // Deep conversion of raw AVs and HVs. + if (SvTYPE(sv) == SVt_PVAV) { + retval = (cfish_Obj*)S_perl_array_to_cfish_array((AV*)sv); + } + else if (SvTYPE(sv) == SVt_PVHV) { + retval = (cfish_Obj*)S_perl_hash_to_cfish_hash((HV*)sv); + } + } + + return retval; +} + +SV* +XSBind_bb_to_sv(const cfish_ByteBuf *bb) { + return bb + ? newSVpvn(Cfish_BB_Get_Buf(bb), Cfish_BB_Get_Size(bb)) + : newSV(0); +} + +SV* +XSBind_cb_to_sv(const cfish_CharBuf *cb) { + if (!cb) { + return newSV(0); + } + else { + SV *sv = newSVpvn((char*)Cfish_CB_Get_Ptr8(cb), Cfish_CB_Get_Size(cb)); + SvUTF8_on(sv); + return sv; + } +} + +static cfish_Hash* +S_perl_hash_to_cfish_hash(HV *phash) { + uint32_t num_keys = hv_iterinit(phash); + cfish_Hash *retval = cfish_Hash_new(num_keys); + cfish_ZombieCharBuf *key = CFISH_ZCB_WRAP_STR("", 0); + + while (num_keys--) { + HE *entry = hv_iternext(phash); + STRLEN key_len = HeKLEN(entry); + SV *value_sv = HeVAL(entry); + cfish_Obj *value = XSBind_perl_to_cfish(value_sv); // Recurse. + + // Force key to UTF-8 if necessary. + if (key_len == (STRLEN)HEf_SVKEY) { + // Key is stored as an SV. Use its UTF-8 flag? Not sure about + // this. + SV *key_sv = HeKEY_sv(entry); + char *key_str = SvPVutf8(key_sv, key_len); + Cfish_ZCB_Assign_Trusted_Str(key, key_str, key_len); + Cfish_Hash_Store(retval, (cfish_Obj*)key, value); + } + else if (HeKUTF8(entry)) { + Cfish_ZCB_Assign_Trusted_Str(key, HeKEY(entry), key_len); + Cfish_Hash_Store(retval, (cfish_Obj*)key, value); + } + else { + char *key_str = HeKEY(entry); + chy_bool_t pure_ascii = true; + for (STRLEN i = 0; i < key_len; i++) { + if ((key_str[i] & 0x80) == 0x80) { pure_ascii = false; } + } + if (pure_ascii) { + Cfish_ZCB_Assign_Trusted_Str(key, key_str, key_len); + Cfish_Hash_Store(retval, (cfish_Obj*)key, value); + } + else { + SV *key_sv = HeSVKEY_force(entry); + key_str = SvPVutf8(key_sv, key_len); + Cfish_ZCB_Assign_Trusted_Str(key, key_str, key_len); + Cfish_Hash_Store(retval, (cfish_Obj*)key, value); + } + } + } + + return retval; +} + +static cfish_VArray* +S_perl_array_to_cfish_array(AV *parray) { + const uint32_t size = av_len(parray) + 1; + cfish_VArray *retval = cfish_VA_new(size); + + // Iterate over array elems. + for (uint32_t i = 0; i < size; i++) { + SV **elem_sv = av_fetch(parray, i, false); + if (elem_sv) { + cfish_Obj *elem = XSBind_perl_to_cfish(*elem_sv); + if (elem) { Cfish_VA_Store(retval, i, elem); } + } + } + Cfish_VA_Resize(retval, size); // needed if last elem is NULL + + return retval; +} + +static SV* +S_cfish_array_to_perl_array(cfish_VArray *varray) { + AV *perl_array = newAV(); + uint32_t num_elems = Cfish_VA_Get_Size(varray); + + // Iterate over array elems. + if (num_elems) { + av_fill(perl_array, num_elems - 1); + for (uint32_t i = 0; i < num_elems; i++) { + cfish_Obj *val = Cfish_VA_Fetch(varray, i); + if (val == NULL) { + continue; + } + else { + // Recurse for each value. + SV *const val_sv = XSBind_cfish_to_perl(val); + av_store(perl_array, i, val_sv); + } + } + } + + return newRV_noinc((SV*)perl_array); +} + +static SV* +S_cfish_hash_to_perl_hash(cfish_Hash *hash) { + HV *perl_hash = newHV(); + SV *key_sv = newSV(1); + cfish_CharBuf *key; + cfish_Obj *val; + + // Prepare the SV key. + SvPOK_on(key_sv); + SvUTF8_on(key_sv); + + // Iterate over key-value pairs. + Cfish_Hash_Iterate(hash); + while (Cfish_Hash_Next(hash, (cfish_Obj**)&key, &val)) { + // Recurse for each value. + SV *val_sv = XSBind_cfish_to_perl(val); + if (!Cfish_Obj_Is_A((cfish_Obj*)key, CFISH_CHARBUF)) { + CFISH_THROW(CFISH_ERR, + "Can't convert a key of class %o to a Perl hash key", + Cfish_Obj_Get_Class_Name((cfish_Obj*)key)); + } + else { + STRLEN key_size = Cfish_CB_Get_Size(key); + char *key_sv_ptr = SvGROW(key_sv, key_size + 1); + memcpy(key_sv_ptr, Cfish_CB_Get_Ptr8(key), key_size); + SvCUR_set(key_sv, key_size); + *SvEND(key_sv) = '\0'; + (void)hv_store_ent(perl_hash, key_sv, val_sv, 0); + } + } + SvREFCNT_dec(key_sv); + + return newRV_noinc((SV*)perl_hash); +} + +struct trap_context { + SV *routine; + SV *context; +}; + +static void +S_attempt_perl_call(void *context) { + struct trap_context *args = (struct trap_context*)context; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(sv_2mortal(newSVsv(args->context))); + PUTBACK; + call_sv(args->routine, G_DISCARD); + FREETMPS; + LEAVE; +} + +cfish_Err* +XSBind_trap(SV *routine, SV *context) { + struct trap_context args; + args.routine = routine; + args.context = context; + return cfish_Err_trap(S_attempt_perl_call, &args); +} + +void +XSBind_enable_overload(void *pobj) { + SV *perl_obj = (SV*)pobj; + HV *stash = SvSTASH(SvRV(perl_obj)); +#if (PERL_VERSION > 10) + Gv_AMupdate(stash, false); +#else + Gv_AMupdate(stash); +#endif + SvAMAGIC_on(perl_obj); +} + +static chy_bool_t +S_extract_from_sv(SV *value, void *target, const char *label, + chy_bool_t required, int type, cfish_VTable *vtable, + void *allocation) { + chy_bool_t valid_assignment = false; + + if (XSBind_sv_defined(value)) { + switch (type) { + case XSBIND_WANT_I8: + *((int8_t*)target) = (int8_t)SvIV(value); + valid_assignment = true; + break; + case XSBIND_WANT_I16: + *((int16_t*)target) = (int16_t)SvIV(value); + valid_assignment = true; + break; + case XSBIND_WANT_I32: + *((int32_t*)target) = (int32_t)SvIV(value); + valid_assignment = true; + break; + case XSBIND_WANT_I64: + if (sizeof(IV) == 8) { + *((int64_t*)target) = (int64_t)SvIV(value); + } + else { // sizeof(IV) == 4 + // lossy. + *((int64_t*)target) = (int64_t)SvNV(value); + } + valid_assignment = true; + break; + case XSBIND_WANT_U8: + *((uint8_t*)target) = (uint8_t)SvUV(value); + valid_assignment = true; + break; + case XSBIND_WANT_U16: + *((uint16_t*)target) = (uint16_t)SvUV(value); + valid_assignment = true; + break; + case XSBIND_WANT_U32: + *((uint32_t*)target) = (uint32_t)SvUV(value); + valid_assignment = true; + break; + case XSBIND_WANT_U64: + if (sizeof(UV) == 8) { + *((uint64_t*)target) = (uint64_t)SvUV(value); + } + else { // sizeof(UV) == 4 + // lossy. + *((uint64_t*)target) = (uint64_t)SvNV(value); + } + valid_assignment = true; + break; + case XSBIND_WANT_BOOL: + *((chy_bool_t*)target) = !!SvTRUE(value); + valid_assignment = true; + break; + case XSBIND_WANT_F32: + *((float*)target) = (float)SvNV(value); + valid_assignment = true; + break; + case XSBIND_WANT_F64: + *((double*)target) = SvNV(value); + valid_assignment = true; + break; + case XSBIND_WANT_OBJ: { + cfish_Obj *object + = XSBind_maybe_sv_to_cfish_obj(value, vtable, + allocation); + if (object) { + *((cfish_Obj**)target) = object; + valid_assignment = true; + } + else { + cfish_CharBuf *mess + = CFISH_MAKE_MESS( + "Invalid value for '%s' - not a %o", + label, Cfish_VTable_Get_Name(vtable)); + cfish_Err_set_error(cfish_Err_new(mess)); + return false; + } + } + break; + case XSBIND_WANT_SV: + *((SV**)target) = value; + valid_assignment = true; + break; + default: { + cfish_CharBuf *mess + = CFISH_MAKE_MESS("Unrecognized type: %i32 for param '%s'", + (int32_t)type, label); + cfish_Err_set_error(cfish_Err_new(mess)); + return false; + } + } + } + + // Enforce that required params cannot be undef and must present valid + // values. + if (required && !valid_assignment) { + cfish_CharBuf *mess = CFISH_MAKE_MESS("Missing required param %s", + label); + cfish_Err_set_error(cfish_Err_new(mess)); + return false; + } + + return true; +} + +chy_bool_t +XSBind_allot_params(SV** stack, int32_t start, int32_t num_stack_elems, ...) { + va_list args; + size_t size = sizeof(int64_t) + num_stack_elems / 64; + void *verified_labels = alloca(size); + memset(verified_labels, 0, size); + + // Verify that our args come in pairs. Return success if there are no + // args. + if ((num_stack_elems - start) % 2 != 0) { + cfish_CharBuf *mess + = CFISH_MAKE_MESS( + "Expecting hash-style params, got odd number of args"); + cfish_Err_set_error(cfish_Err_new(mess)); + return false; + } + + void *target; + va_start(args, num_stack_elems); + while (NULL != (target = va_arg(args, void*))) { + char *label = va_arg(args, char*); + int label_len = va_arg(args, int); + int required = va_arg(args, int); + int type = va_arg(args, int); + cfish_VTable *vtable = va_arg(args, cfish_VTable*); + void *allocation = va_arg(args, void*); + + // Iterate through the stack looking for labels which match this param + // name. If the label appears more than once, keep track of where it + // appears *last*, as the last time a param appears overrides all + // previous appearances. + int32_t found_arg = -1; + for (int32_t tick = start; tick < num_stack_elems; tick += 2) { + SV *const key_sv = stack[tick]; + if (SvCUR(key_sv) == (STRLEN)label_len) { + if (memcmp(SvPVX(key_sv), label, label_len) == 0) { + found_arg = tick; + lucy_NumUtil_u1set(verified_labels, tick); + } + } + } + + if (found_arg == -1) { + // Didn't find this parameter. Throw an error if it was required. + if (required) { + cfish_CharBuf *mess + = CFISH_MAKE_MESS("Missing required parameter: '%s'", + label); + cfish_Err_set_error(cfish_Err_new(mess)); + return false; + } + } + else { + // Found the arg. Extract the value. + SV *value = stack[found_arg + 1]; + chy_bool_t got_arg = S_extract_from_sv(value, target, label, + required, type, vtable, + allocation); + if (!got_arg) { + CFISH_ERR_ADD_FRAME(cfish_Err_get_error()); + return false; + } + } + } + va_end(args); + + // Ensure that all parameter labels were valid. + for (int32_t tick = start; tick < num_stack_elems; tick += 2) { + if (!lucy_NumUtil_u1get(verified_labels, tick)) { + SV *const key_sv = stack[tick]; + char *key = SvPV_nolen(key_sv); + cfish_CharBuf *mess + = CFISH_MAKE_MESS("Invalid parameter: '%s'", key); + cfish_Err_set_error(cfish_Err_new(mess)); + return false; + } + } + + return true; +} + + diff --git a/clownfish/perl/xs/XSBind.h b/clownfish/perl/xs/XSBind.h new file mode 100644 index 000000000..9348fc54a --- /dev/null +++ b/clownfish/perl/xs/XSBind.h @@ -0,0 +1,349 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* XSBind.h -- Functions to help bind Clownfish to Perl XS api. + */ + +#ifndef H_CFISH_XSBIND +#define H_CFISH_XSBIND 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include "charmony.h" +#include "Clownfish/Obj.h" +#include "Clownfish/ByteBuf.h" +#include "Clownfish/CharBuf.h" +#include "Clownfish/Err.h" +#include "Clownfish/Hash.h" +#include "Clownfish/Num.h" +#include "Clownfish/VArray.h" +#include "Clownfish/VTable.h" + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#define NEED_newRV_noinc_GLOBAL +#include "ppport.h" + +#define XSBIND_EXTERN CHY_EXPORT + +/** Given either a class name or a perl object, manufacture a new Clownfish + * object suitable for supplying to a cfish_Foo_init() function. + */ +XSBIND_EXTERN cfish_Obj* +cfish_XSBind_new_blank_obj(SV *either_sv); + +/** Test whether an SV is defined. Handles "get" magic, unlike SvOK on its + * own. + */ +static CHY_INLINE chy_bool_t +cfish_XSBind_sv_defined(SV *sv) { + if (!sv || !SvANY(sv)) { return false; } + if (SvGMAGICAL(sv)) { mg_get(sv); } + return SvOK(sv); +} + +/** If the SV contains a Clownfish object which passes an "isa" test against the + * passed-in VTable, return a pointer to it. If not, but + * allocation is non-NULL and a ZombieCharBuf would satisfy the + * "isa" test, stringify the SV, create a ZombieCharBuf using + * allocation, assign the SV's string to it, and return that + * instead. If all else fails, throw an exception. + */ +XSBIND_EXTERN cfish_Obj* +cfish_XSBind_sv_to_cfish_obj(SV *sv, cfish_VTable *vtable, void *allocation); + +/** As XSBind_sv_to_cfish_obj above, but returns NULL instead of throwing an + * exception. + */ +XSBIND_EXTERN cfish_Obj* +cfish_XSBind_maybe_sv_to_cfish_obj(SV *sv, cfish_VTable *vtable, + void *allocation); + + +/** Derive an SV from a Clownfish object. If the Clownfish object is NULL, the SV + * will be undef. + * + * The new SV has single refcount for which the caller must take + * responsibility. + */ +static CHY_INLINE SV* +cfish_XSBind_cfish_obj_to_sv(cfish_Obj *obj) { + return obj ? (SV*)Cfish_Obj_To_Host(obj) : newSV(0); +} + +/** XSBind_cfish_obj_to_sv, with a cast. + */ +#define CFISH_OBJ_TO_SV(_obj) cfish_XSBind_cfish_obj_to_sv((cfish_Obj*)_obj) + +/** As XSBind_cfish_obj_to_sv above, except decrements the object's refcount + * after creating the SV. This is useful when the Clownfish expression creates a new + * refcount, e.g. a call to a constructor. + */ +static CHY_INLINE SV* +cfish_XSBind_cfish_obj_to_sv_noinc(cfish_Obj *obj) { + SV *retval; + if (obj) { + retval = (SV*)Cfish_Obj_To_Host(obj); + Cfish_Obj_Dec_RefCount(obj); + } + else { + retval = newSV(0); + } + return retval; +} + +/** XSBind_cfish_obj_to_sv_noinc, with a cast. + */ +#define CFISH_OBJ_TO_SV_NOINC(_obj) \ + cfish_XSBind_cfish_obj_to_sv_noinc((cfish_Obj*)_obj) + +/** Deep conversion of Clownfish objects to Perl objects -- CharBufs to UTF-8 + * SVs, ByteBufs to SVs, VArrays to Perl array refs, Hashes to Perl hashrefs, + * and any other object to a Perl object wrapping the Clownfish Obj. + */ +XSBIND_EXTERN SV* +cfish_XSBind_cfish_to_perl(cfish_Obj *obj); + +/** Deep conversion of Perl data structures to Clownfish objects -- Perl hash + * to Hash, Perl array to VArray, Clownfish objects stripped of their + * wrappers, and everything else stringified and turned to a CharBuf. + */ +XSBIND_EXTERN cfish_Obj* +cfish_XSBind_perl_to_cfish(SV *sv); + +/** Convert a ByteBuf into a new string SV. + */ +XSBIND_EXTERN SV* +cfish_XSBind_bb_to_sv(const cfish_ByteBuf *bb); + +/** Convert a CharBuf into a new UTF-8 string SV. + */ +XSBIND_EXTERN SV* +cfish_XSBind_cb_to_sv(const cfish_CharBuf *cb); + +/** Perl-specific wrapper for Err#trap. The "routine" must be either a + * subroutine reference or the name of a subroutine. + */ +cfish_Err* +cfish_XSBind_trap(SV *routine, SV *context); + +/** Turn on overloading for the supplied Perl object and its class. + */ +XSBIND_EXTERN void +cfish_XSBind_enable_overload(void *pobj); + +/** Process hash-style params passed to an XS subroutine. The varargs must be + * a NULL-terminated series of ALLOT_ macros. + * + * cfish_XSBind_allot_params(stack, start, num_stack_elems, + * ALLOT_OBJ(&field, "field", 5, LUCY_CHARBUF, true, alloca(cfish_ZCB_size()), + * ALLOT_OBJ(&term, "term", 4, LUCY_CHARBUF, true, alloca(cfish_ZCB_size()), + * NULL); + * + * The following ALLOT_ macros are available for primitive types: + * + * ALLOT_I8(ptr, key, keylen, required) + * ALLOT_I16(ptr, key, keylen, required) + * ALLOT_I32(ptr, key, keylen, required) + * ALLOT_I64(ptr, key, keylen, required) + * ALLOT_U8(ptr, key, keylen, required) + * ALLOT_U16(ptr, key, keylen, required) + * ALLOT_U32(ptr, key, keylen, required) + * ALLOT_U64(ptr, key, keylen, required) + * ALLOT_BOOL(ptr, key, keylen, required) + * ALLOT_CHAR(ptr, key, keylen, required) + * ALLOT_SHORT(ptr, key, keylen, required) + * ALLOT_INT(ptr, key, keylen, required) + * ALLOT_LONG(ptr, key, keylen, required) + * ALLOT_SIZE_T(ptr, key, keylen, required) + * ALLOT_F32(ptr, key, keylen, required) + * ALLOT_F64(ptr, key, keylen, required) + * + * The four arguments to these ALLOT_ macros have the following meanings: + * + * ptr -- A pointer to the variable to be extracted. + * key -- The name of the parameter as a C string. + * keylen -- The length of the parameter name in bytes. + * required -- A boolean indicating whether the parameter is required. + * + * If a required parameter is not present, allot_params() will set Err_error + * and return false. + * + * Use the following macro if a Clownfish object is desired: + * + * ALLOT_OBJ(ptr, key, keylen, required, vtable, allocation) + * + * The "vtable" argument must be the VTable corresponding to the class of the + * desired object. The "allocation" argument must be a blob of memory + * allocated on the stack sufficient to hold a ZombieCharBuf. (Use + * cfish_ZCB_size() to find the allocation size.) + * + * To extract a Perl scalar, use the following ALLOT_ macro: + * + * ALLOT_SV(ptr, key, keylen, required) + * + * All possible valid param names must be passed via the ALLOT_ macros; if a + * user-supplied param cannot be matched up with an ALLOT_ macro, + * allot_params() will set Err_error and return false. + * + * @param stack The Perl stack. + * @param start Where on the Perl stack to start looking for params. For + * methods, this would typically be 1; for functions, most likely 0. + * @param num_stack_elems The number of arguments passed to the Perl function + * (generally, the XS variable "items"). + * @return true on success, false on failure (sets Err_error). + */ +XSBIND_EXTERN chy_bool_t +cfish_XSBind_allot_params(SV** stack, int32_t start, + int32_t num_stack_elems, ...); + +#define XSBIND_WANT_I8 0x1 +#define XSBIND_WANT_I16 0x2 +#define XSBIND_WANT_I32 0x3 +#define XSBIND_WANT_I64 0x4 +#define XSBIND_WANT_U8 0x5 +#define XSBIND_WANT_U16 0x6 +#define XSBIND_WANT_U32 0x7 +#define XSBIND_WANT_U64 0x8 +#define XSBIND_WANT_BOOL 0x9 +#define XSBIND_WANT_F32 0xA +#define XSBIND_WANT_F64 0xB +#define XSBIND_WANT_OBJ 0xC +#define XSBIND_WANT_SV 0xD + +#if (CHY_SIZEOF_CHAR == 1) + #define XSBIND_WANT_CHAR XSBIND_WANT_I8 +#else + #error "Can't build unless sizeof(char) == 1" +#endif + +#if (CHY_SIZEOF_SHORT == 2) + #define XSBIND_WANT_SHORT XSBIND_WANT_I16 +#else + #error "Can't build unless sizeof(short) == 2" +#endif + +#if (CHY_SIZEOF_INT == 4) + #define XSBIND_WANT_INT XSBIND_WANT_I32 +#else // sizeof(int) == 8 + #define XSBIND_WANT_INT XSBIND_WANT_I64 +#endif + +#if (CHY_SIZEOF_LONG == 4) + #define XSBIND_WANT_LONG XSBIND_WANT_I32 +#else // sizeof(long) == 8 + #define XSBIND_WANT_LONG XSBIND_WANT_I64 +#endif + +#if (CHY_SIZEOF_SIZE_T == 4) + #define XSBIND_WANT_SIZE_T XSBIND_WANT_U32 +#else // sizeof(long) == 8 + #define XSBIND_WANT_SIZE_T XSBIND_WANT_U64 +#endif + +#define XSBIND_ALLOT_I8(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_I8, NULL, NULL +#define XSBIND_ALLOT_I16(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_I16, NULL, NULL +#define XSBIND_ALLOT_I32(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_I32, NULL, NULL +#define XSBIND_ALLOT_I64(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_I64, NULL, NULL +#define XSBIND_ALLOT_U8(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_U8, NULL, NULL +#define XSBIND_ALLOT_U16(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_U16, NULL, NULL +#define XSBIND_ALLOT_U32(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_U32, NULL, NULL +#define XSBIND_ALLOT_U64(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_U64, NULL, NULL +#define XSBIND_ALLOT_BOOL(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_BOOL, NULL, NULL +#define XSBIND_ALLOT_CHAR(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_CHAR, NULL, NULL +#define XSBIND_ALLOT_SHORT(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_SHORT, NULL, NULL +#define XSBIND_ALLOT_INT(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_INT, NULL, NULL +#define XSBIND_ALLOT_LONG(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_LONG, NULL, NULL +#define XSBIND_ALLOT_SIZE_T(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_SIZE_T, NULL, NULL +#define XSBIND_ALLOT_F32(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_F32, NULL, NULL +#define XSBIND_ALLOT_F64(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_F64, NULL, NULL +#define XSBIND_ALLOT_OBJ(ptr, key, keylen, required, vtable, allocation) \ + ptr, key, keylen, required, XSBIND_WANT_OBJ, vtable, allocation +#define XSBIND_ALLOT_SV(ptr, key, keylen, required) \ + ptr, key, keylen, required, XSBIND_WANT_SV, NULL, NULL + +/* Define short names for most of the symbols in this file. Note that these + * short names are ALWAYS in effect, since they are only used for Perl and we + * can be confident they don't conflict with anything. (It's prudent to use + * full symbols nevertheless in case someone else defines e.g. a function + * named "XSBind_sv_defined".) + */ +#define XSBind_new_blank_obj cfish_XSBind_new_blank_obj +#define XSBind_sv_defined cfish_XSBind_sv_defined +#define XSBind_sv_to_cfish_obj cfish_XSBind_sv_to_cfish_obj +#define XSBind_maybe_sv_to_cfish_obj cfish_XSBind_maybe_sv_to_cfish_obj +#define XSBind_cfish_obj_to_sv cfish_XSBind_cfish_obj_to_sv +#define XSBind_cfish_obj_to_sv_noinc cfish_XSBind_cfish_obj_to_sv_noinc +#define XSBind_cfish_to_perl cfish_XSBind_cfish_to_perl +#define XSBind_perl_to_cfish cfish_XSBind_perl_to_cfish +#define XSBind_bb_to_sv cfish_XSBind_bb_to_sv +#define XSBind_cb_to_sv cfish_XSBind_cb_to_sv +#define XSBind_trap cfish_XSBind_trap +#define XSBind_enable_overload cfish_XSBind_enable_overload +#define XSBind_allot_params cfish_XSBind_allot_params +#define ALLOT_I8 XSBIND_ALLOT_I8 +#define ALLOT_I16 XSBIND_ALLOT_I16 +#define ALLOT_I32 XSBIND_ALLOT_I32 +#define ALLOT_I64 XSBIND_ALLOT_I64 +#define ALLOT_U8 XSBIND_ALLOT_U8 +#define ALLOT_U16 XSBIND_ALLOT_U16 +#define ALLOT_U32 XSBIND_ALLOT_U32 +#define ALLOT_U64 XSBIND_ALLOT_U64 +#define ALLOT_BOOL XSBIND_ALLOT_BOOL +#define ALLOT_CHAR XSBIND_ALLOT_CHAR +#define ALLOT_SHORT XSBIND_ALLOT_SHORT +#define ALLOT_INT XSBIND_ALLOT_INT +#define ALLOT_LONG XSBIND_ALLOT_LONG +#define ALLOT_SIZE_T XSBIND_ALLOT_SIZE_T +#define ALLOT_F32 XSBIND_ALLOT_F32 +#define ALLOT_F64 XSBIND_ALLOT_F64 +#define ALLOT_OBJ XSBIND_ALLOT_OBJ +#define ALLOT_SV XSBIND_ALLOT_SV + +/* Strip the prefix from some common ClownFish symbols where we know there's + * no conflict with Perl. It's a little inconsistent to do this rather than + * leave all symbols at full size, but the succinctness is worth it. + */ +#define THROW CFISH_THROW +#define WARN CFISH_WARN + +#ifdef __cplusplus +} +#endif + +#endif // H_CFISH_XSBIND + + From c9852a6bb710d7aa1763be00b0b97690b5259c20 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 19 Sep 2012 15:15:52 -0700 Subject: [PATCH 3/9] Get charmonize and CFC to build. --- clownfish/perl/Build.PL | 12 +++---- clownfish/perl/buildlib/Clownfish/Build.pm | 37 ++++++++++++---------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/clownfish/perl/Build.PL b/clownfish/perl/Build.PL index 6b25c44e1..78744ea60 100644 --- a/clownfish/perl/Build.PL +++ b/clownfish/perl/Build.PL @@ -18,9 +18,9 @@ use strict; use warnings; use lib 'buildlib'; use File::Spec::Functions qw( catdir ); -use Lucy::Build; +use Clownfish::Build; -my @BASE_PATH = Lucy::Build->cf_base_path; +my @BASE_PATH = Clownfish::Build->cf_base_path; my $MODULES_DIR = catdir( @BASE_PATH, 'modules' ); my $SNOWSTEM_SRC_DIR = catdir( $MODULES_DIR, qw( analysis snowstem source ) ); my $SNOWSTEM_INC_DIR = catdir( $SNOWSTEM_SRC_DIR, 'include' ); @@ -30,8 +30,8 @@ my $UTF8PROC_SRC_DIR = catdir( $MODULES_DIR, qw( unicode utf8proc ) ); my $CORE_SOURCE_DIR = catdir( @BASE_PATH, 'core' ); my $XS_SOURCE_DIR = 'xs'; -my $builder = Lucy::Build->new( - module_name => 'Lucy', +my $builder = Clownfish::Build->new( + module_name => 'Clownfish', license => 'apache', dist_author => 'The Apache Lucy Project ', @@ -44,7 +44,7 @@ my $builder = Lucy::Build->new( 'ExtUtils::ParseXS' => 2.18, 'Devel::PPPort' => 3.13, }, - meta_merge => { keywords => [qw( search lucy lucene )], }, + meta_merge => { keywords => [qw( clownfish )], }, meta_add => { resources => { homepage => 'http://lucy.apache.org', @@ -70,7 +70,7 @@ my $builder = Lucy::Build->new( }, add_to_cleanup => [ qw( - Lucy-* + Clownfish-* MANIFEST.bak perltidy.ERR *.pdb diff --git a/clownfish/perl/buildlib/Clownfish/Build.pm b/clownfish/perl/buildlib/Clownfish/Build.pm index 006ffba29..cb379dacf 100644 --- a/clownfish/perl/buildlib/Clownfish/Build.pm +++ b/clownfish/perl/buildlib/Clownfish/Build.pm @@ -16,21 +16,21 @@ use strict; use warnings; -use lib '../clownfish/cfc/perl/blib/arch'; -use lib '../clownfish/cfc/perl/blib/lib'; -use lib 'clownfish/cfc/perl/blib/arch'; -use lib 'clownfish/cfc/perl/blib/lib'; +use lib '../cfc/perl/blib/arch'; +use lib '../cfc/perl/blib/lib'; +use lib 'cfc/perl/blib/arch'; +use lib 'cfc/perl/blib/lib'; -package Lucy::Build; +package Clownfish::Build; -# We want to subclass Clownfish::CFC::Perl::Build, but Clownfish might not be +# We want to subclass Clownfish::CFC::Perl::Build, but CFC might not be # built yet. So we look in 'clownfish/cfc/perl/lib' directly and cleanup @INC # afterwards. -use lib '../clownfish/cfc/perl/lib'; -use lib 'clownfish/cfc/perl/lib'; +use lib '../cfc/perl/lib'; +use lib 'cfc/perl/lib'; use base qw( Clownfish::CFC::Perl::Build ); -no lib '../clownfish/cfc/perl/lib'; -no lib 'clownfish/cfc/perl/lib'; +no lib '../cfc/perl/lib'; +no lib 'cfc/perl/lib'; our $VERSION = '0.003000'; $VERSION = eval $VERSION; @@ -47,15 +47,15 @@ BEGIN { unshift @PATH, rel2abs( getcwd() ) } my @BASE_PATH = __PACKAGE__->cf_base_path; -my $CHARMONIZER_ORIG_DIR = catdir( @BASE_PATH, 'charmonizer' ); +my $CHARMONIZER_ORIG_DIR = rel2abs( catdir( @BASE_PATH, updir(), 'charmonizer') ); my $CHARMONIZE_EXE_PATH = "charmonize$Config{_exe}"; my $CHARMONY_H_PATH = 'charmony.h'; my $CHARMONY_PM_PATH = 'Charmony.pm'; -my $LEMON_DIR = catdir( @BASE_PATH, 'lemon' ); +my $LEMON_DIR = rel2abs( catdir( @BASE_PATH, updir(), 'lemon' ) ); my $LEMON_EXE_PATH = catfile( $LEMON_DIR, "lemon$Config{_exe}" ); my $CORE_SOURCE_DIR = catdir( @BASE_PATH, 'core' ); -my $CLOWNFISH_DIR = catdir( @BASE_PATH, 'clownfish', 'cfc', 'perl' ); -my $CLOWNFISH_BUILD = catfile( $CLOWNFISH_DIR, 'Build' ); +my $CFC_DIR = catdir( @BASE_PATH, 'cfc', 'perl' ); +my $CFC_BUILD = catfile( $CFC_DIR, 'Build' ); my $LIB_DIR = 'lib'; sub new { @@ -109,6 +109,8 @@ sub _run_make { # Build the charmonize executable. sub ACTION_build_charmonize { my $self = shift; + warn __PACKAGE__->cf_base_path; + print "Building $CHARMONIZE_EXE_PATH...\n\n"; my $meld_c = rel2abs("charmonize.c"); $self->add_to_cleanup($meld_c); @@ -196,7 +198,8 @@ sub ACTION_lemon { sub ACTION_cfc { my $self = shift; my $old_dir = getcwd(); - chdir($CLOWNFISH_DIR); + chdir($CFC_DIR); + warn $CFC_DIR; if ( !-f 'Build' ) { print "\nBuilding Clownfish compiler... \n"; system("$^X Build.PL"); @@ -528,9 +531,9 @@ sub ACTION_semiclean { # Run the cleanup targets for independent prerequisite builds. sub _clean_prereq_builds { my $self = shift; - if ( -e $CLOWNFISH_BUILD ) { + if ( -e $CFC_BUILD ) { my $old_dir = getcwd(); - chdir $CLOWNFISH_DIR; + chdir $CFC_DIR; system("$^X Build realclean") and die "Clownfish clean failed"; chdir $old_dir; From c55d66dd674d29fe18f6aa327c25838d0300df77 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 19 Sep 2012 16:23:02 -0700 Subject: [PATCH 4/9] Remove superfluous build subroutines. --- clownfish/perl/buildlib/Clownfish/Build.pm | 167 --------------------- 1 file changed, 167 deletions(-) diff --git a/clownfish/perl/buildlib/Clownfish/Build.pm b/clownfish/perl/buildlib/Clownfish/Build.pm index cb379dacf..3799efd99 100644 --- a/clownfish/perl/buildlib/Clownfish/Build.pm +++ b/clownfish/perl/buildlib/Clownfish/Build.pm @@ -51,8 +51,6 @@ my $CHARMONIZER_ORIG_DIR = rel2abs( catdir( @BASE_PATH, updir(), 'charmonizer') my $CHARMONIZE_EXE_PATH = "charmonize$Config{_exe}"; my $CHARMONY_H_PATH = 'charmony.h'; my $CHARMONY_PM_PATH = 'Charmony.pm'; -my $LEMON_DIR = rel2abs( catdir( @BASE_PATH, updir(), 'lemon' ) ); -my $LEMON_EXE_PATH = catfile( $LEMON_DIR, "lemon$Config{_exe}" ); my $CORE_SOURCE_DIR = catdir( @BASE_PATH, 'core' ); my $CFC_DIR = catdir( @BASE_PATH, 'cfc', 'perl' ); my $CFC_BUILD = catfile( $CFC_DIR, 'Build' ); @@ -185,16 +183,6 @@ sub ACTION_charmonizer_tests { ); } -# Build the Lemon parser generator. -sub ACTION_lemon { - my $self = shift; - print "Building the Lemon parser generator...\n\n"; - $self->_run_make( - dir => $LEMON_DIR, - args => [], - ); -} - sub ACTION_cfc { my $self = shift; my $old_dir = getcwd(); @@ -345,30 +333,6 @@ sub ACTION_test_valgrind { } } -# Run all .y files through lemon. -sub ACTION_parsers { - my $self = shift; - $self->dispatch('lemon'); - my $y_files = $self->rscan_dir( $CORE_SOURCE_DIR, qr/\.y$/ ); - for my $y_file (@$y_files) { - my $c_file = $y_file; - my $h_file = $y_file; - $c_file =~ s/\.y$/.c/ or die "no match"; - $h_file =~ s/\.y$/.h/ or die "no match"; - next if $self->up_to_date( $y_file, [ $c_file, $h_file ] ); - $self->add_to_cleanup( $c_file, $h_file ); - system( $LEMON_EXE_PATH, '-q', $y_file ) and die "lemon failed"; - } -} - -sub ACTION_compile_custom_xs { - my $self = shift; - - $self->dispatch('parsers'); - - $self->SUPER::ACTION_compile_custom_xs; -} - sub autogen_header { my $self = shift; return <<"END_AUTOGEN"; @@ -399,135 +363,6 @@ sub autogen_header { END_AUTOGEN } -sub _check_module_build_for_dist { - eval "use Module::Build 0.38;"; - die "./Build dist reqiures Module::Build 0.38 or higher--this is only " - . Module::Build->VERSION . $/ if $@; -} - -sub ACTION_distdir { - _check_module_build_for_dist; - shift->SUPER::ACTION_distdir(@_); -} - -sub ACTION_dist { - my $self = shift; - _check_module_build_for_dist; - - # Create POD but make sure not to include build artifacts. - $self->dispatch('pod'); - _clean_prereq_builds($self); - - # We build our Perl release tarball from $REPOS_ROOT/perl, rather than - # from the top-level. - # - # Because some items we need are outside this directory, we need to copy a - # bunch of stuff. After the tarball is packaged up, we delete the copied - # directories. - my @items_to_copy = qw( - core - modules - charmonizer - devel - clownfish - lemon - CHANGES - CONTRIBUTING - LICENSE - NOTICE - README - ); - print "Copying files...\n"; - - for my $item (@items_to_copy) { - confess("'$item' already exists") if -e $item; - system("cp -R ../$item $item"); - } - - $self->dispatch('manifest'); - my $no_index = $self->_gen_pause_exclusion_list; - my $meta_add = $self->meta_add || {}; - $meta_add->{no_index} = $no_index; - $self->meta_add( $meta_add ); - $self->SUPER::ACTION_dist; - - # Clean up. - print "Removing copied files...\n"; - rmtree($_) for @items_to_copy; - unlink("META.yml"); - move( "MANIFEST.bak", "MANIFEST" ) or die "move() failed: $!"; -} - -sub ACTION_distmeta { - my $self = shift; - $self->SUPER::ACTION_distmeta(@_); - # Make sure everything has a version. - require CPAN::Meta; - my $v = version->new($self->dist_version); - my $meta = CPAN::Meta->load_file('META.json'); - my $provides = $meta->provides; - while (my ($pkg, $data) = each %{ $provides }) { - die "$pkg, defined in $data->{file}, has no version\n" - unless $data->{version}; - die "$pkg, defined in $data->{file}, is " - . version->new($data->{version})->normal - . " but should be " . $v->normal . "\n" - unless $data->{version} == $v; - } -} - -# Generate a list of files for PAUSE, search.cpan.org, etc to ignore. -sub _gen_pause_exclusion_list { - my $self = shift; - - # Only exclude files that are actually on-board. - open( my $man_fh, '<', 'MANIFEST' ) or die "Can't open MANIFEST: $!"; - my @manifest_entries = <$man_fh>; - chomp @manifest_entries; - - my @excluded_files; - for my $entry (@manifest_entries) { - # Allow README and Changes. - next if $entry =~ m#^(README|Changes)#; - - # Allow public modules. - if ( $entry =~ m#^(perl/)?lib\b.+\.(pm|pod)$# ) { - open( my $fh, '<', $entry ) or die "Can't open '$entry': $!"; - my $content = do { local $/; <$fh> }; - next if $content =~ /=head1\s*NAME/; - } - - # Disallow everything else. - push @excluded_files, $entry; - } - - # Exclude redacted modules. - if ( eval { require "buildlib/Lucy/Redacted.pm" } ) { - my @redacted = map { - my @parts = split( /\W+/, $_ ); - catfile( $LIB_DIR, @parts ) . '.pm' - } Lucy::Redacted->redacted, Lucy::Redacted->hidden; - push @excluded_files, @redacted; - } - - my %uniquifier; - @excluded_files = sort grep { !$uniquifier{$_}++ } @excluded_files; - return { file => \@excluded_files }; -} - -sub ACTION_semiclean { - my $self = shift; - print "Cleaning up most build files.\n"; - my @candidates - = grep { $_ !~ /(charmonizer|^_charm|charmony|charmonize|snowstem)/ } - $self->cleanup; - for my $path ( map { glob($_) } @candidates ) { - next unless -e $path; - rmtree($path); - confess("Failed to remove '$path'") if -e $path; - } -} - # Run the cleanup targets for independent prerequisite builds. sub _clean_prereq_builds { my $self = shift; @@ -539,7 +374,6 @@ sub _clean_prereq_builds { chdir $old_dir; } $self->_run_make( dir => $CHARMONIZER_ORIG_DIR, args => ['clean'] ); - $self->_run_make( dir => $LEMON_DIR, args => ['clean'] ); } sub ACTION_clean { @@ -550,4 +384,3 @@ sub ACTION_clean { 1; -__END__ From 0bf05e5706a93c85c5394fab7a8d35334418c2a8 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 19 Sep 2012 16:23:42 -0700 Subject: [PATCH 5/9] Adapt Clownfish::Build::Binding for new layout. --- .../perl/buildlib/Clownfish/Build/Binding.pm | 125 +----------------- 1 file changed, 1 insertion(+), 124 deletions(-) diff --git a/clownfish/perl/buildlib/Clownfish/Build/Binding.pm b/clownfish/perl/buildlib/Clownfish/Build/Binding.pm index 095129584..b39a128d5 100644 --- a/clownfish/perl/buildlib/Clownfish/Build/Binding.pm +++ b/clownfish/perl/buildlib/Clownfish/Build/Binding.pm @@ -12,7 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -package Lucy::Build::Binding::Object; +package Clownfish::Build::Binding; use strict; use warnings; @@ -21,13 +21,11 @@ $VERSION = eval $VERSION; sub bind_all { my $class = shift; - $class->bind_bitvector; $class->bind_bytebuf; $class->bind_charbuf; $class->bind_err; $class->bind_hash; $class->bind_host; - $class->bind_i32array; $class->bind_lockfreeregistry; $class->bind_float32; $class->bind_float64; @@ -36,51 +34,6 @@ sub bind_all { $class->bind_vtable; } -sub bind_bitvector { - my @exposed = qw( - Get - Set - Clear - Clear_All - And - Or - And_Not - Xor - Flip - Flip_Block - Next_Hit - To_Array - Grow - Count - ); - - my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; - my $synopsis = <<'END_SYNOPSIS'; - my $bit_vec = Lucy::Object::BitVector->new( capacity => 8 ); - my $other = Lucy::Object::BitVector->new( capacity => 8 ); - $bit_vec->set($_) for ( 0, 2, 4, 6 ); - $other->set($_) for ( 1, 3, 5, 7 ); - $bit_vec->or($other); - print "$_\n" for @{ $bit_vec->to_array }; # prints 0 through 7. -END_SYNOPSIS - my $constructor = <<'END_CONSTRUCTOR'; - my $bit_vec = Lucy::Object::BitVector->new( - capacity => $doc_max + 1, # default 0, - ); -END_CONSTRUCTOR - $pod_spec->set_synopsis($synopsis); - $pod_spec->add_constructor( alias => 'new', sample => $constructor ); - $pod_spec->add_method( method => $_, alias => lc($_) ) for @exposed; - - my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", - class_name => "Lucy::Object::BitVector", - ); - $binding->set_pod_spec($pod_spec); - - Clownfish::CFC::Binding::Perl::Class->register($binding); -} - sub bind_bytebuf { my $xs_code = <<'END_XS_CODE'; MODULE = Lucy PACKAGE = Clownfish::ByteBuf @@ -395,82 +348,6 @@ END_XS_CODE Clownfish::CFC::Binding::Perl::Class->register($binding); } -sub bind_i32array { - my $xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Lucy::Object::I32Array - -SV* -new(either_sv, ...) - SV *either_sv; -CODE: -{ - SV *ints_sv = NULL; - lucy_I32Array *self = NULL; - - chy_bool_t args_ok - = XSBind_allot_params(&(ST(0)), 1, items, - ALLOT_SV(&ints_sv, "ints", 4, true), - NULL); - if (!args_ok) { - CFISH_RETHROW(CFISH_INCREF(cfish_Err_get_error())); - } - - AV *ints_av = NULL; - if (SvROK(ints_sv)) { - ints_av = (AV*)SvRV(ints_sv); - } - if (ints_av && SvTYPE(ints_av) == SVt_PVAV) { - int32_t size = av_len(ints_av) + 1; - int32_t *ints = (int32_t*)LUCY_MALLOCATE(size * sizeof(int32_t)); - int32_t i; - - for (i = 0; i < size; i++) { - SV **const sv_ptr = av_fetch(ints_av, i, 0); - ints[i] = (sv_ptr && XSBind_sv_defined(*sv_ptr)) - ? SvIV(*sv_ptr) - : 0; - } - self = (lucy_I32Array*)XSBind_new_blank_obj(either_sv); - lucy_I32Arr_init(self, ints, size); - } - else { - THROW(LUCY_ERR, "Required param 'ints' isn't an arrayref"); - } - - RETVAL = CFISH_OBJ_TO_SV_NOINC(self); -} -OUTPUT: RETVAL - -SV* -to_arrayref(self) - lucy_I32Array *self; -CODE: -{ - AV *out_av = newAV(); - uint32_t i; - uint32_t size = Lucy_I32Arr_Get_Size(self); - - av_extend(out_av, size); - for (i = 0; i < size; i++) { - int32_t result = Lucy_I32Arr_Get(self, i); - SV* result_sv = result == -1 ? newSV(0) : newSViv(result); - av_push(out_av, result_sv); - } - RETVAL = newRV_noinc((SV*)out_av); -} -OUTPUT: RETVAL -END_XS_CODE - - my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", - class_name => "Lucy::Object::I32Array", - ); - $binding->append_xs($xs_code); - $binding->exclude_constructor; - - Clownfish::CFC::Binding::Perl::Class->register($binding); -} - sub bind_lockfreeregistry { my $binding = Clownfish::CFC::Binding::Perl::Class->new( parcel => "Lucy", From 851f0b1050147f97118d142f5c5844fa097b7401 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 19 Sep 2012 16:47:29 -0700 Subject: [PATCH 6/9] Remove serialization capabilities. --- clownfish/core/Clownfish/ByteBuf.c | 18 --- clownfish/core/Clownfish/ByteBuf.cfh | 6 - clownfish/core/Clownfish/CharBuf.c | 21 ---- clownfish/core/Clownfish/CharBuf.cfh | 6 - clownfish/core/Clownfish/Hash.c | 66 ----------- clownfish/core/Clownfish/Hash.cfh | 6 - clownfish/core/Clownfish/Num.c | 62 ---------- clownfish/core/Clownfish/Num.cfh | 30 ----- clownfish/core/Clownfish/Obj.c | 20 ---- clownfish/core/Clownfish/Obj.cfh | 12 -- clownfish/core/Clownfish/VArray.c | 34 ------ clownfish/core/Clownfish/VArray.cfh | 6 - .../perl/buildlib/Clownfish/Build/Binding.pm | 110 ------------------ 13 files changed, 397 deletions(-) diff --git a/clownfish/core/Clownfish/ByteBuf.c b/clownfish/core/Clownfish/ByteBuf.c index 776266843..024b06ce1 100644 --- a/clownfish/core/Clownfish/ByteBuf.c +++ b/clownfish/core/Clownfish/ByteBuf.c @@ -27,8 +27,6 @@ #include "Clownfish/VTable.h" #include "Clownfish/ByteBuf.h" #include "Clownfish/Err.h" -#include "Lucy/Store/InStream.h" -#include "Lucy/Store/OutStream.h" #include "Clownfish/Util/Memory.h" static void @@ -192,22 +190,6 @@ BB_grow(ByteBuf *self, size_t size) { return self->buf; } -void -BB_serialize(ByteBuf *self, OutStream *target) { - OutStream_Write_C32(target, self->size); - OutStream_Write_Bytes(target, self->buf, self->size); -} - -ByteBuf* -BB_deserialize(ByteBuf *self, InStream *instream) { - const size_t size = InStream_Read_C32(instream); - const size_t capacity = size ? size : sizeof(int64_t); - if (capacity > self->cap) { S_grow(self, capacity); } - self->size = size; - InStream_Read_Bytes(instream, self->buf, size); - return self; -} - int BB_compare(const void *va, const void *vb) { const ByteBuf *a = *(const ByteBuf**)va; diff --git a/clownfish/core/Clownfish/ByteBuf.cfh b/clownfish/core/Clownfish/ByteBuf.cfh index 1b9b61f00..e155ce7b0 100644 --- a/clownfish/core/Clownfish/ByteBuf.cfh +++ b/clownfish/core/Clownfish/ByteBuf.cfh @@ -119,12 +119,6 @@ class Clownfish::ByteBuf cnick BB inherits Clownfish::Obj { public int32_t Hash_Sum(ByteBuf *self); - - public void - Serialize(ByteBuf *self, OutStream *outstream); - - public incremented ByteBuf* - Deserialize(decremented ByteBuf *self, InStream *instream); } /** diff --git a/clownfish/core/Clownfish/CharBuf.c b/clownfish/core/Clownfish/CharBuf.c index 553ce863f..627aa5862 100644 --- a/clownfish/core/Clownfish/CharBuf.c +++ b/clownfish/core/Clownfish/CharBuf.c @@ -29,8 +29,6 @@ #include "Clownfish/CharBuf.h" #include "Clownfish/Err.h" -#include "Lucy/Store/InStream.h" -#include "Lucy/Store/OutStream.h" #include "Clownfish/Util/Memory.h" #include "Clownfish/Util/StringHelper.h" @@ -449,25 +447,6 @@ CB_load(CharBuf *self, Obj *dump) { return CB_Clone(source); } -void -CB_serialize(CharBuf *self, OutStream *target) { - OutStream_Write_C32(target, self->size); - OutStream_Write_Bytes(target, self->ptr, self->size); -} - -CharBuf* -CB_deserialize(CharBuf *self, InStream *instream) { - size_t size = InStream_Read_C32(instream); - if (size >= self->cap) { S_grow(self, size); } - InStream_Read_Bytes(instream, self->ptr, size); - self->size = size; - self->ptr[size] = '\0'; - if (!StrHelp_utf8_valid(self->ptr, size)) { - DIE_INVALID_UTF8(self->ptr, size); - } - return self; -} - void CB_mimic_str(CharBuf *self, const char* ptr, size_t size) { if (!StrHelp_utf8_valid(ptr, size)) { diff --git a/clownfish/core/Clownfish/CharBuf.cfh b/clownfish/core/Clownfish/CharBuf.cfh index 5acc0e68d..8075f7469 100644 --- a/clownfish/core/Clownfish/CharBuf.cfh +++ b/clownfish/core/Clownfish/CharBuf.cfh @@ -235,12 +235,6 @@ class Clownfish::CharBuf cnick CB public incremented CharBuf* Load(CharBuf *self, Obj *dump); - public void - Serialize(CharBuf *self, OutStream *outstream); - - public incremented CharBuf* - Deserialize(decremented CharBuf *self, InStream *instream); - /** Remove Unicode whitespace characters from both top and tail. */ uint32_t diff --git a/clownfish/core/Clownfish/Hash.c b/clownfish/core/Clownfish/Hash.c index 2f71e5fff..3b7355338 100644 --- a/clownfish/core/Clownfish/Hash.c +++ b/clownfish/core/Clownfish/Hash.c @@ -28,9 +28,6 @@ #include "Clownfish/CharBuf.h" #include "Clownfish/Err.h" #include "Clownfish/VArray.h" -#include "Lucy/Store/InStream.h" -#include "Lucy/Store/OutStream.h" -#include "Lucy/Util/Freezer.h" #include "Clownfish/Util/Memory.h" static HashTombStone *TOMBSTONE; @@ -168,69 +165,6 @@ Hash_load(Hash *self, Obj *dump) { } -void -Hash_serialize(Hash *self, OutStream *outstream) { - Obj *key; - Obj *val; - uint32_t charbuf_count = 0; - OutStream_Write_C32(outstream, self->size); - - // Write CharBuf keys first. CharBuf keys are the common case; grouping - // them together is a form of run-length-encoding and saves space, since - // we omit the per-key class name. - Hash_Iterate(self); - while (Hash_Next(self, &key, &val)) { - if (Obj_Is_A(key, CHARBUF)) { charbuf_count++; } - } - OutStream_Write_C32(outstream, charbuf_count); - Hash_Iterate(self); - while (Hash_Next(self, &key, &val)) { - if (Obj_Is_A(key, CHARBUF)) { - Obj_Serialize(key, outstream); - FREEZE(val, outstream); - } - } - - // Punt on the classes of the remaining keys. - Hash_Iterate(self); - while (Hash_Next(self, &key, &val)) { - if (!Obj_Is_A(key, CHARBUF)) { - FREEZE(key, outstream); - FREEZE(val, outstream); - } - } -} - -Hash* -Hash_deserialize(Hash *self, InStream *instream) { - uint32_t size = InStream_Read_C32(instream); - uint32_t num_charbufs = InStream_Read_C32(instream); - uint32_t num_other = size - num_charbufs; - CharBuf *key = num_charbufs ? CB_new(0) : NULL; - - Hash_init(self, size); - - // Read key-value pairs with CharBuf keys. - while (num_charbufs--) { - uint32_t len = InStream_Read_C32(instream); - char *key_buf = CB_Grow(key, len); - InStream_Read_Bytes(instream, key_buf, len); - key_buf[len] = '\0'; - CB_Set_Size(key, len); - Hash_Store(self, (Obj*)key, THAW(instream)); - } - DECREF(key); - - // Read remaining key/value pairs. - while (num_other--) { - Obj *k = THAW(instream); - Hash_Store(self, k, THAW(instream)); - DECREF(k); - } - - return self; -} - void Hash_clear(Hash *self) { HashEntry *entry = (HashEntry*)self->entries; diff --git a/clownfish/core/Clownfish/Hash.cfh b/clownfish/core/Clownfish/Hash.cfh index a75be6390..cec9ff9b1 100644 --- a/clownfish/core/Clownfish/Hash.cfh +++ b/clownfish/core/Clownfish/Hash.cfh @@ -139,12 +139,6 @@ class Clownfish::Hash inherits Clownfish::Obj { public incremented Obj* Load(Hash *self, Obj *dump); - public void - Serialize(Hash *self, OutStream *outstream); - - public incremented Hash* - Deserialize(decremented Hash *self, InStream *instream); - public void Destroy(Hash *self); } diff --git a/clownfish/core/Clownfish/Num.c b/clownfish/core/Clownfish/Num.c index e01f96df6..8a226eeb0 100644 --- a/clownfish/core/Clownfish/Num.c +++ b/clownfish/core/Clownfish/Num.c @@ -30,8 +30,6 @@ #include "Clownfish/CharBuf.h" #include "Clownfish/Err.h" #include "Clownfish/VTable.h" -#include "Lucy/Store/InStream.h" -#include "Lucy/Store/OutStream.h" Num* Num_init(Num *self) { @@ -146,17 +144,6 @@ Float32_mimic(Float32 *self, Obj *other) { self->value = twin->value; } -void -Float32_serialize(Float32 *self, OutStream *outstream) { - OutStream_Write_F32(outstream, self->value); -} - -Float32* -Float32_deserialize(Float32 *self, InStream *instream) { - float value = InStream_Read_F32(instream); - return Float32_init(self, value); -} - /***************************************************************************/ Float64* @@ -208,17 +195,6 @@ Float64_hash_sum(Float64 *self) { return ints[0] ^ ints[1]; } -void -Float64_serialize(Float64 *self, OutStream *outstream) { - OutStream_Write_F64(outstream, self->value); -} - -Float64* -Float64_deserialize(Float64 *self, InStream *instream) { - double value = InStream_Read_F64(instream); - return Float64_init(self, value); -} - /***************************************************************************/ Integer32* @@ -269,17 +245,6 @@ Int32_hash_sum(Integer32 *self) { return self->value; } -void -Int32_serialize(Integer32 *self, OutStream *outstream) { - OutStream_Write_C32(outstream, (uint32_t)self->value); -} - -Integer32* -Int32_deserialize(Integer32 *self, InStream *instream) { - int32_t value = (int32_t)InStream_Read_C32(instream); - return Int32_init(self, value); -} - /***************************************************************************/ Integer64* @@ -348,17 +313,6 @@ Int64_equals(Integer64 *self, Obj *other) { return true; } -void -Int64_serialize(Integer64 *self, OutStream *outstream) { - OutStream_Write_C64(outstream, (uint64_t)self->value); -} - -Integer64* -Int64_deserialize(Integer64 *self, InStream *instream) { - int64_t value = (int64_t)InStream_Read_C64(instream); - return Int64_init(self, value); -} - /***************************************************************************/ @@ -428,22 +382,6 @@ Bool_equals(BoolNum *self, Obj *other) { return self == (BoolNum*)other; } -void -Bool_serialize(BoolNum *self, OutStream *outstream) { - OutStream_Write_U8(outstream, (uint8_t)self->value); -} - -BoolNum* -Bool_deserialize(BoolNum *self, InStream *instream) { - bool_t value = (bool_t)InStream_Read_U8(instream); - if (self && self != CFISH_TRUE && self != CFISH_FALSE) { - Bool_Dec_RefCount_t super_decref - = SUPER_METHOD_PTR(BOOLNUM, Lucy_Bool_Dec_RefCount); - super_decref(self); - } - return value ? CFISH_TRUE : CFISH_FALSE; -} - BoolNum* Bool_inc_refcount(BoolNum *self) { return self; diff --git a/clownfish/core/Clownfish/Num.cfh b/clownfish/core/Clownfish/Num.cfh index b702758dd..f25c547e9 100644 --- a/clownfish/core/Clownfish/Num.cfh +++ b/clownfish/core/Clownfish/Num.cfh @@ -86,12 +86,6 @@ class Clownfish::Float32 inherits Clownfish::FloatNum { public int32_t Hash_Sum(Float32 *self); - public void - Serialize(Float32 *self, OutStream *outstream); - - public incremented Float32* - Deserialize(decremented Float32 *self, InStream *instream); - public incremented Float32* Clone(Float32 *self); @@ -129,12 +123,6 @@ class Clownfish::Float64 inherits Clownfish::FloatNum { public int32_t Hash_Sum(Float64 *self); - public void - Serialize(Float64 *self, OutStream *outstream); - - public incremented Float64* - Deserialize(decremented Float64 *self, InStream *instream); - public incremented Float64* Clone(Float64 *self); @@ -173,12 +161,6 @@ class Clownfish::Integer32 cnick Int32 public int32_t Hash_Sum(Integer32 *self); - public void - Serialize(Integer32 *self, OutStream *outstream); - - public incremented Integer32* - Deserialize(decremented Integer32 *self, InStream *instream); - public incremented Integer32* Clone(Integer32 *self); @@ -221,12 +203,6 @@ class Clownfish::Integer64 cnick Int64 public bool_t Equals(Integer64 *self, Obj *other); - public void - Serialize(Integer64 *self, OutStream *outstream); - - public incremented Integer64* - Deserialize(decremented Integer64 *self, InStream *instream); - public incremented Integer64* Clone(Integer64 *self); @@ -275,12 +251,6 @@ class Clownfish::BoolNum cnick Bool inherits Clownfish::IntNum { public int32_t Hash_Sum(BoolNum *self); - public void - Serialize(BoolNum *self, OutStream *outstream); - - public incremented BoolNum* - Deserialize(decremented BoolNum *self, InStream *instream); - /* Returns self. */ public incremented BoolNum* Clone(BoolNum *self); diff --git a/clownfish/core/Clownfish/Obj.c b/clownfish/core/Clownfish/Obj.c index 7b44435e3..7bfab34df 100644 --- a/clownfish/core/Clownfish/Obj.c +++ b/clownfish/core/Clownfish/Obj.c @@ -28,8 +28,6 @@ #include "Clownfish/Err.h" #include "Clownfish/Hash.h" #include "Clownfish/VTable.h" -#include "Lucy/Store/InStream.h" -#include "Lucy/Store/OutStream.h" #include "Clownfish/Util/Memory.h" Obj* @@ -68,24 +66,6 @@ Obj_equals(Obj *self, Obj *other) { return (self == other); } -void -Obj_serialize(Obj *self, OutStream *outstream) { - CharBuf *class_name = Obj_Get_Class_Name(self); - CB_Serialize(class_name, outstream); -} - -Obj* -Obj_deserialize(Obj *self, InStream *instream) { - CharBuf *class_name - = CB_Deserialize((CharBuf*)VTable_Make_Obj(CHARBUF), instream); - CharBuf *my_class = VTable_Get_Name(self->vtable); - if (!CB_Equals(class_name, (Obj*)my_class)) { - THROW(ERR, "Class mismatch: %o %o", class_name, my_class); - } - DECREF(class_name); - return Obj_init(self); -} - CharBuf* Obj_to_string(Obj *self) { #if (SIZEOF_PTR == 4) diff --git a/clownfish/core/Clownfish/Obj.cfh b/clownfish/core/Clownfish/Obj.cfh index de57b2e4f..614edae8e 100644 --- a/clownfish/core/Clownfish/Obj.cfh +++ b/clownfish/core/Clownfish/Obj.cfh @@ -153,18 +153,6 @@ public class Clownfish::Obj { public bool_t To_Bool(Obj *self); - /** Serialize the object by writing to the supplied OutStream. - */ - public void - Serialize(Obj *self, OutStream *outstream); - - /** Inflate an object by reading the serialized form from the instream. - * The assumption is that the object has been allocated, assigned a - * refcount and a vtable, but that everything else is uninitialized. - */ - public incremented Obj* - Deserialize(decremented Obj *self, InStream *instream); - /** Return a representation of the object using only scalars, hashes, and * arrays. Some implementations support JSON serialization via Dump() and * its companion method, Load(); for others, Dump() is only a debugging diff --git a/clownfish/core/Clownfish/VArray.c b/clownfish/core/Clownfish/VArray.c index cb137791f..fd4b6f755 100644 --- a/clownfish/core/Clownfish/VArray.c +++ b/clownfish/core/Clownfish/VArray.c @@ -25,10 +25,7 @@ #include "Clownfish/VArray.h" #include "Clownfish/Err.h" #include "Clownfish/Util/Memory.h" -#include "Lucy/Util/Freezer.h" #include "Clownfish/Util/SortUtils.h" -#include "Lucy/Store/InStream.h" -#include "Lucy/Store/OutStream.h" VArray* VA_new(uint32_t capacity) { @@ -90,37 +87,6 @@ VA_load(VArray *self, Obj *dump) { return loaded; } -void -VA_serialize(VArray *self, OutStream *outstream) { - uint32_t last_valid_tick = 0; - OutStream_Write_C32(outstream, self->size); - for (uint32_t i = 0; i < self->size; i++) { - Obj *elem = self->elems[i]; - if (elem) { - OutStream_Write_C32(outstream, i - last_valid_tick); - FREEZE(elem, outstream); - last_valid_tick = i; - } - } - // Terminate. - OutStream_Write_C32(outstream, self->size - last_valid_tick); -} - -VArray* -VA_deserialize(VArray *self, InStream *instream) { - uint32_t size = InStream_Read_C32(instream); - VA_Grow(self, size); - for (uint32_t tick = InStream_Read_C32(instream); - tick < size; - tick += InStream_Read_C32(instream) - ) { - Obj *obj = THAW(instream); - self->elems[tick] = obj; - } - self->size = size; - return self; -} - VArray* VA_clone(VArray *self) { VArray *twin = VA_new(self->size); diff --git a/clownfish/core/Clownfish/VArray.cfh b/clownfish/core/Clownfish/VArray.cfh index dcc045517..3b4695ae2 100644 --- a/clownfish/core/Clownfish/VArray.cfh +++ b/clownfish/core/Clownfish/VArray.cfh @@ -168,12 +168,6 @@ class Clownfish::VArray cnick VA inherits Clownfish::Obj { public incremented VArray* Load(VArray *self, Obj *dump); - public void - Serialize(VArray *self, OutStream *outstream); - - public incremented VArray* - Deserialize(decremented VArray *self, InStream *instream); - public void Destroy(VArray *self); } diff --git a/clownfish/perl/buildlib/Clownfish/Build/Binding.pm b/clownfish/perl/buildlib/Clownfish/Build/Binding.pm index b39a128d5..97581fa94 100644 --- a/clownfish/perl/buildlib/Clownfish/Build/Binding.pm +++ b/clownfish/perl/buildlib/Clownfish/Build/Binding.pm @@ -52,15 +52,6 @@ CODE: RETVAL = CFISH_OBJ_TO_SV_NOINC(self); } OUTPUT: RETVAL - -SV* -_deserialize(self, instream) - lucy_ByteBuf *self; - lucy_InStream *instream; -CODE: - lucy_ByteBuf *thawed = Lucy_BB_Deserialize(self, instream); - RETVAL = (SV*)Lucy_BB_To_Host(thawed); -OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( @@ -99,15 +90,6 @@ CODE: RETVAL = CFISH_OBJ_TO_SV_NOINC(lucy_CB_clone(self)); OUTPUT: RETVAL -SV* -_deserialize(self, instream) - lucy_CharBuf *self; - lucy_InStream *instream; -CODE: - lucy_CharBuf *thawed = Lucy_CB_Deserialize(self, instream); - RETVAL = (SV*)Lucy_CB_To_Host(thawed); -OUTPUT: RETVAL - SV* to_perl(self) lucy_CharBuf *self; @@ -200,16 +182,6 @@ sub bind_hash { my $xs_code = <<'END_XS_CODE'; MODULE = Lucy PACKAGE = Clownfish::Hash - -SV* -_deserialize(self, instream) - lucy_Hash *self; - lucy_InStream *instream; -CODE: - lucy_Hash *thawed = Lucy_Hash_Deserialize(self, instream); - RETVAL = (SV*)Lucy_Hash_To_Host(thawed); -OUTPUT: RETVAL - SV* _fetch(self, key) lucy_Hash *self; @@ -523,79 +495,6 @@ CODE: RETVAL = Lucy_Obj_Is_A(self, target); } OUTPUT: RETVAL - -void -STORABLE_freeze(self, ...) - lucy_Obj *self; -PPCODE: -{ - CHY_UNUSED_VAR(self); - if (items < 2 || !SvTRUE(ST(1))) { - SV *retval; - lucy_ByteBuf *serialized_bb; - lucy_RAMFileHandle *file_handle - = lucy_RAMFH_open(NULL, LUCY_FH_WRITE_ONLY | LUCY_FH_CREATE, NULL); - lucy_OutStream *target = lucy_OutStream_open((lucy_Obj*)file_handle); - - Lucy_Obj_Serialize(self, target); - - Lucy_OutStream_Close(target); - serialized_bb - = Lucy_RAMFile_Get_Contents(Lucy_RAMFH_Get_File(file_handle)); - retval = XSBind_bb_to_sv(serialized_bb); - CFISH_DECREF(file_handle); - CFISH_DECREF(target); - - if (SvCUR(retval) == 0) { // Thwart Storable bug - THROW(LUCY_ERR, "Calling serialize produced an empty string"); - } - ST(0) = sv_2mortal(retval); - XSRETURN(1); - } -} - -=begin comment - -Calls deserialize(), and copies the object pointer. Since deserialize is an -abstract method, it will confess() unless implemented. - -=end comment - -=cut - -void -STORABLE_thaw(blank_obj, cloning, serialized_sv) - SV *blank_obj; - SV *cloning; - SV *serialized_sv; -PPCODE: -{ - char *class_name = HvNAME(SvSTASH(SvRV(blank_obj))); - lucy_ZombieCharBuf *klass - = CFISH_ZCB_WRAP_STR(class_name, strlen(class_name)); - lucy_VTable *vtable - = (lucy_VTable*)lucy_VTable_singleton((lucy_CharBuf*)klass, NULL); - STRLEN len; - char *ptr = SvPV(serialized_sv, len); - lucy_ViewByteBuf *contents = lucy_ViewBB_new(ptr, len); - lucy_RAMFile *ram_file = lucy_RAMFile_new((lucy_ByteBuf*)contents, true); - lucy_RAMFileHandle *file_handle - = lucy_RAMFH_open(NULL, LUCY_FH_READ_ONLY, ram_file); - lucy_InStream *instream = lucy_InStream_open((lucy_Obj*)file_handle); - lucy_Obj *self = Lucy_VTable_Foster_Obj(vtable, blank_obj); - lucy_Obj *deserialized = Lucy_Obj_Deserialize(self, instream); - - CHY_UNUSED_VAR(cloning); - CFISH_DECREF(contents); - CFISH_DECREF(ram_file); - CFISH_DECREF(file_handle); - CFISH_DECREF(instream); - - // Catch bad deserialize() override. - if (deserialized != self) { - THROW(LUCY_ERR, "Error when deserializing obj of class %o", klass); - } -} END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( @@ -630,15 +529,6 @@ CODE: RETVAL = CFISH_OBJ_TO_SV_NOINC(Lucy_VA_Shallow_Copy(self)); OUTPUT: RETVAL -SV* -_deserialize(self, instream) - lucy_VArray *self; - lucy_InStream *instream; -CODE: - lucy_VArray *thawed = Lucy_VA_Deserialize(self, instream); - RETVAL = (SV*)Lucy_VA_To_Host(thawed); -OUTPUT: RETVAL - SV* _clone(self) lucy_VArray *self; From 55a37493585dbf66b57cdf485f4c4607318661ed Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Thu, 20 Sep 2012 17:13:12 -0700 Subject: [PATCH 7/9] Remove references to Lucy/Util/ToolSet.h. --- clownfish/perl/buildlib/Clownfish/Build.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/clownfish/perl/buildlib/Clownfish/Build.pm b/clownfish/perl/buildlib/Clownfish/Build.pm index 3799efd99..375880b3f 100644 --- a/clownfish/perl/buildlib/Clownfish/Build.pm +++ b/clownfish/perl/buildlib/Clownfish/Build.pm @@ -206,7 +206,6 @@ sub ACTION_copy_clownfish_includes { $self->cf_copy_include_file( 'charmony.h' ); $self->cf_copy_include_file( 'XSBind.h' ); - $self->cf_copy_include_file( qw( Lucy Util ToolSet.h ) ); } sub ACTION_clownfish { From d8114a907784065a8e88027864e9589bfc976c40 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Thu, 20 Sep 2012 17:14:00 -0700 Subject: [PATCH 8/9] Purge Lucy from Clownfish.pm. --- clownfish/perl/lib/Clownfish.pm | 268 +------------------------------- 1 file changed, 3 insertions(+), 265 deletions(-) diff --git a/clownfish/perl/lib/Clownfish.pm b/clownfish/perl/lib/Clownfish.pm index e8b222ca6..11ecc7f57 100644 --- a/clownfish/perl/lib/Clownfish.pm +++ b/clownfish/perl/lib/Clownfish.pm @@ -16,7 +16,7 @@ use strict; use warnings; -package Lucy; +package Clownfish; use 5.008003; use Exporter; @@ -33,34 +33,13 @@ BEGIN { require DynaLoader; our @ISA = qw( DynaLoader Exporter ); # This loads a large number of disparate subs. - bootstrap Lucy '0.3.0'; + bootstrap Clownfish '0.3.0'; _init_autobindings(); our @EXPORT_OK = qw( to_clownfish to_perl kdump ); } -sub kdump { - require Data::Dumper; - my $kdumper = Data::Dumper->new( [@_] ); - $kdumper->Sortkeys( sub { return [ sort keys %{ $_[0] } ] } ); - $kdumper->Indent(1); - warn $kdumper->Dump; -} - sub error {$Clownfish::Err::error} -{ - package Lucy::Util::IndexFileNames; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - BEGIN { - push our @ISA, 'Exporter'; - our @EXPORT_OK = qw( - extract_gen - latest_snapshot - ); - } -} - { package Clownfish::Util::StringHelper; our $VERSION = '0.003000'; @@ -79,55 +58,6 @@ sub error {$Clownfish::Err::error} } } -{ - # Temporary back compat. - package Lucy::Object::Obj; - BEGIN { our @ISA = qw( Clownfish::Obj ) } -} - -{ - package Lucy::Analysis::RegexTokenizer; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - - sub compile_token_re { return qr/$_[1]/ } - - sub new { - my ( $either, %args ) = @_; - my $token_re = delete $args{token_re}; - $args{pattern} = "$token_re" if $token_re; - return $either->_new(%args); - } -} - -{ - package Lucy::Document::Doc; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - use Storable qw( nfreeze thaw ); - use bytes; - no bytes; - - use overload - fallback => 1, - '%{}' => \&get_fields; - - sub serialize_fields { - my ( $self, $outstream ) = @_; - my $buf = nfreeze( $self->get_fields ); - $outstream->write_c32( bytes::length($buf) ); - $outstream->print($buf); - } - - sub deserialize_fields { - my ( $self, $instream ) = @_; - my $len = $instream->read_c32; - my $buf; - $instream->read( $buf, $len ); - $self->set_fields( thaw($buf) ); - } -} - { package Clownfish::LockFreeRegistry; our $VERSION = '0.003000'; @@ -140,7 +70,7 @@ sub error {$Clownfish::Err::error} package Clownfish::Obj; our $VERSION = '0.003000'; $VERSION = eval $VERSION; - use Lucy qw( to_clownfish to_perl ); + use Clownfish qw( to_clownfish to_perl ); sub load { return $_[0]->_load( to_clownfish( $_[1] ) ) } } @@ -186,115 +116,6 @@ sub error {$Clownfish::Err::error} sub DESTROY { } # leak all } -{ - package Lucy::Index::Indexer; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - - sub new { - my ( $either, %args ) = @_; - my $flags = 0; - $flags |= CREATE if delete $args{'create'}; - $flags |= TRUNCATE if delete $args{'truncate'}; - return $either->_new( %args, flags => $flags ); - } -} - -{ - package Lucy::Index::IndexReader; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - use Carp; - - sub new { - confess( - "IndexReader is an abstract class; use open() instead of new()"); - } - sub lexicon { - my $self = shift; - my $lex_reader = $self->fetch("Lucy::Index::LexiconReader"); - return $lex_reader->lexicon(@_) if $lex_reader; - return; - } - sub posting_list { - my $self = shift; - my $plist_reader = $self->fetch("Lucy::Index::PostingListReader"); - return $plist_reader->posting_list(@_) if $plist_reader; - return; - } - sub offsets { shift->_offsets->to_arrayref } -} - -{ - package Lucy::Index::Segment; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - use Lucy qw( to_clownfish ); - sub store_metadata { - my ( $self, %args ) = @_; - $self->_store_metadata( %args, - metadata => to_clownfish( $args{metadata} ) ); - } -} - -{ - package Lucy::Search::Compiler; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - use Carp; - use Scalar::Util qw( blessed ); - - sub new { - my ( $either, %args ) = @_; - if ( !defined $args{boost} ) { - confess("'parent' is not a Query") - unless ( blessed( $args{parent} ) - and $args{parent}->isa("Lucy::Search::Query") ); - $args{boost} = $args{parent}->get_boost; - } - return $either->do_new(%args); - } -} - -{ - package Lucy::Search::Query; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - - sub make_compiler { - my ( $self, %args ) = @_; - $args{boost} = $self->get_boost unless defined $args{boost}; - return $self->_make_compiler(%args); - } -} - -{ - package Lucy::Search::SortRule; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - use Carp; - - my %types = ( - field => FIELD(), - score => SCORE(), - doc_id => DOC_ID(), - ); - - sub new { - my ( $either, %args ) = @_; - my $type = delete $args{type} || 'field'; - confess("Invalid type: '$type'") unless defined $types{$type}; - return $either->_new( %args, type => $types{$type} ); - } -} - -{ - package Lucy::Object::BitVector; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - sub to_arrayref { shift->to_array->to_arrayref } -} - { package Clownfish::ByteBuf; our $VERSION = '0.003000'; @@ -404,86 +225,6 @@ sub error {$Clownfish::Err::error} sub deserialize { CORE::shift->_deserialize(@_) } } -{ - package Lucy::Store::FileHandle; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - BEGIN { - push our @ISA, 'Exporter'; - our @EXPORT_OK = qw( build_fh_flags ); - } - - sub build_fh_flags { - my $args = shift; - my $flags = 0; - $flags |= FH_CREATE if delete $args->{create}; - $flags |= FH_READ_ONLY if delete $args->{read_only}; - $flags |= FH_WRITE_ONLY if delete $args->{write_only}; - $flags |= FH_EXCLUSIVE if delete $args->{exclusive}; - return $flags; - } - - sub open { - my ( $either, %args ) = @_; - $args{flags} ||= 0; - $args{flags} |= build_fh_flags( \%args ); - return $either->_open(%args); - } -} - -{ - package Lucy::Store::FSFileHandle; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - - sub open { - my ( $either, %args ) = @_; - $args{flags} ||= 0; - $args{flags} |= Lucy::Store::FileHandle::build_fh_flags( \%args ); - return $either->_open(%args); - } -} - -{ - package Lucy::Store::FSFolder; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - use File::Spec::Functions qw( rel2abs ); - sub absolutify { return rel2abs( $_[1] ) } -} - -{ - package Lucy::Store::RAMFileHandle; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - - sub open { - my ( $either, %args ) = @_; - $args{flags} ||= 0; - $args{flags} |= Lucy::Store::FileHandle::build_fh_flags( \%args ); - return $either->_open(%args); - } -} - -{ - package Lucy::Util::Debug; - our $VERSION = '0.003000'; - $VERSION = eval $VERSION; - BEGIN { - push our @ISA, 'Exporter'; - our @EXPORT_OK = qw( - DEBUG - DEBUG_PRINT - DEBUG_ENABLED - ASSERT - set_env_cache - num_allocated - num_freed - num_globals - ); - } -} - { package Clownfish::Host; our $VERSION = '0.003000'; @@ -497,6 +238,3 @@ sub error {$Clownfish::Err::error} 1; -__END__ - - From f9932dc0c300f98e4ca4ca80719b096979a9275f Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Fri, 21 Sep 2012 12:06:00 -0700 Subject: [PATCH 9/9] Change parcel from Lucy to Clownfish. --- clownfish/core/Clownfish.cfp | 5 +++ clownfish/core/Clownfish/ByteBuf.cfh | 2 +- clownfish/core/Clownfish/CharBuf.cfh | 2 +- clownfish/core/Clownfish/Err.cfh | 2 +- clownfish/core/Clownfish/Hash.cfh | 2 +- clownfish/core/Clownfish/Host.cfh | 2 +- clownfish/core/Clownfish/LockFreeRegistry.cfh | 2 +- clownfish/core/Clownfish/Method.cfh | 2 +- clownfish/core/Clownfish/Num.cfh | 2 +- clownfish/core/Clownfish/Obj.cfh | 2 +- clownfish/core/Clownfish/Util/Atomic.cfh | 2 +- clownfish/core/Clownfish/Util/Memory.cfh | 2 +- clownfish/core/Clownfish/Util/NumberUtils.cfh | 2 +- clownfish/core/Clownfish/Util/SortUtils.cfh | 2 +- .../core/Clownfish/Util/StringHelper.cfh | 2 +- clownfish/core/Clownfish/VArray.cfh | 2 +- clownfish/core/Clownfish/VTable.cfh | 2 +- .../perl/buildlib/Clownfish/Build/Binding.pm | 44 +++++++++---------- 18 files changed, 43 insertions(+), 38 deletions(-) create mode 100644 clownfish/core/Clownfish.cfp diff --git a/clownfish/core/Clownfish.cfp b/clownfish/core/Clownfish.cfp new file mode 100644 index 000000000..cd4b1e08b --- /dev/null +++ b/clownfish/core/Clownfish.cfp @@ -0,0 +1,5 @@ +{ + "name": "Clownfish", + "nickname": "Lucy", + "version": "v0.3.0" +} diff --git a/clownfish/core/Clownfish/ByteBuf.cfh b/clownfish/core/Clownfish/ByteBuf.cfh index e155ce7b0..303bc252f 100644 --- a/clownfish/core/Clownfish/ByteBuf.cfh +++ b/clownfish/core/Clownfish/ByteBuf.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** * Growable buffer holding arbitrary bytes. diff --git a/clownfish/core/Clownfish/CharBuf.cfh b/clownfish/core/Clownfish/CharBuf.cfh index 8075f7469..6183b5ff2 100644 --- a/clownfish/core/Clownfish/CharBuf.cfh +++ b/clownfish/core/Clownfish/CharBuf.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** * Growable buffer holding Unicode characters. diff --git a/clownfish/core/Clownfish/Err.cfh b/clownfish/core/Clownfish/Err.cfh index 51f5b8b7c..3042773c6 100644 --- a/clownfish/core/Clownfish/Err.cfh +++ b/clownfish/core/Clownfish/Err.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; __C__ typedef void diff --git a/clownfish/core/Clownfish/Hash.cfh b/clownfish/core/Clownfish/Hash.cfh index cec9ff9b1..262c0cbb4 100644 --- a/clownfish/core/Clownfish/Hash.cfh +++ b/clownfish/core/Clownfish/Hash.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** * Hashtable. diff --git a/clownfish/core/Clownfish/Host.cfh b/clownfish/core/Clownfish/Host.cfh index 203c6bbac..07ad891ee 100644 --- a/clownfish/core/Clownfish/Host.cfh +++ b/clownfish/core/Clownfish/Host.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; __C__ #define CFISH_HOST_ARGTYPE_I32 (int32_t)0x00000001 diff --git a/clownfish/core/Clownfish/LockFreeRegistry.cfh b/clownfish/core/Clownfish/LockFreeRegistry.cfh index a801011e7..2939b9aec 100644 --- a/clownfish/core/Clownfish/LockFreeRegistry.cfh +++ b/clownfish/core/Clownfish/LockFreeRegistry.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** Specialized lock free hash table for storing VTables. */ diff --git a/clownfish/core/Clownfish/Method.cfh b/clownfish/core/Clownfish/Method.cfh index b2476ec89..e33791fbd 100644 --- a/clownfish/core/Clownfish/Method.cfh +++ b/clownfish/core/Clownfish/Method.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** Method metadata. */ diff --git a/clownfish/core/Clownfish/Num.cfh b/clownfish/core/Clownfish/Num.cfh index f25c547e9..b81f88bdf 100644 --- a/clownfish/core/Clownfish/Num.cfh +++ b/clownfish/core/Clownfish/Num.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** Abstract base class for numbers. */ diff --git a/clownfish/core/Clownfish/Obj.cfh b/clownfish/core/Clownfish/Obj.cfh index 614edae8e..fe491b103 100644 --- a/clownfish/core/Clownfish/Obj.cfh +++ b/clownfish/core/Clownfish/Obj.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** Base class for all objects. */ diff --git a/clownfish/core/Clownfish/Util/Atomic.cfh b/clownfish/core/Clownfish/Util/Atomic.cfh index c673c1c65..dfd2fcbdb 100644 --- a/clownfish/core/Clownfish/Util/Atomic.cfh +++ b/clownfish/core/Clownfish/Util/Atomic.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** Provide atomic memory operations. */ diff --git a/clownfish/core/Clownfish/Util/Memory.cfh b/clownfish/core/Clownfish/Util/Memory.cfh index cd249b4cc..8a8bf2f20 100644 --- a/clownfish/core/Clownfish/Util/Memory.cfh +++ b/clownfish/core/Clownfish/Util/Memory.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; inert class Clownfish::Util::Memory { diff --git a/clownfish/core/Clownfish/Util/NumberUtils.cfh b/clownfish/core/Clownfish/Util/NumberUtils.cfh index e820f5bab..dc92e87d2 100644 --- a/clownfish/core/Clownfish/Util/NumberUtils.cfh +++ b/clownfish/core/Clownfish/Util/NumberUtils.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** Provide various number-related utilies. * diff --git a/clownfish/core/Clownfish/Util/SortUtils.cfh b/clownfish/core/Clownfish/Util/SortUtils.cfh index 4071a32b5..d1f19b283 100644 --- a/clownfish/core/Clownfish/Util/SortUtils.cfh +++ b/clownfish/core/Clownfish/Util/SortUtils.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; __C__ typedef int diff --git a/clownfish/core/Clownfish/Util/StringHelper.cfh b/clownfish/core/Clownfish/Util/StringHelper.cfh index cd97f3c50..7b6b2c6db 100644 --- a/clownfish/core/Clownfish/Util/StringHelper.cfh +++ b/clownfish/core/Clownfish/Util/StringHelper.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; inert class Clownfish::Util::StringHelper cnick StrHelp { diff --git a/clownfish/core/Clownfish/VArray.cfh b/clownfish/core/Clownfish/VArray.cfh index 3b4695ae2..3d49f2c22 100644 --- a/clownfish/core/Clownfish/VArray.cfh +++ b/clownfish/core/Clownfish/VArray.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; __C__ #include "Clownfish/Util/SortUtils.h" diff --git a/clownfish/core/Clownfish/VTable.cfh b/clownfish/core/Clownfish/VTable.cfh index 6308d7b8a..e8aba56dd 100644 --- a/clownfish/core/Clownfish/VTable.cfh +++ b/clownfish/core/Clownfish/VTable.cfh @@ -14,7 +14,7 @@ * limitations under the License. */ -parcel Lucy; +parcel Clownfish; /** Virtual method dispatch table. * diff --git a/clownfish/perl/buildlib/Clownfish/Build/Binding.pm b/clownfish/perl/buildlib/Clownfish/Build/Binding.pm index 97581fa94..e4c5bdddc 100644 --- a/clownfish/perl/buildlib/Clownfish/Build/Binding.pm +++ b/clownfish/perl/buildlib/Clownfish/Build/Binding.pm @@ -36,7 +36,7 @@ sub bind_all { sub bind_bytebuf { my $xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::ByteBuf +MODULE = Clownfish PACKAGE = Clownfish::ByteBuf SV* new(either_sv, sv) @@ -55,7 +55,7 @@ OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::ByteBuf", ); $binding->append_xs($xs_code); @@ -66,7 +66,7 @@ END_XS_CODE sub bind_charbuf { my $xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::CharBuf +MODULE = Clownfish PACKAGE = Clownfish::CharBuf SV* new(either_sv, sv) @@ -97,7 +97,7 @@ CODE: RETVAL = XSBind_cb_to_sv(self); OUTPUT: RETVAL -MODULE = Lucy PACKAGE = Clownfish::ViewCharBuf +MODULE = Clownfish PACKAGE = Clownfish::ViewCharBuf SV* _new(unused, sv) @@ -116,7 +116,7 @@ OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::CharBuf", ); $binding->append_xs($xs_code); @@ -151,7 +151,7 @@ END_SYNOPSIS $pod_spec->set_synopsis($synopsis); my $xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::Err +MODULE = Clownfish PACKAGE = Clownfish::Err SV* trap(routine_sv, context_sv) @@ -164,7 +164,7 @@ OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::Err", ); $binding->bind_constructor( alias => '_new' ); @@ -181,7 +181,7 @@ sub bind_hash { ); my $xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::Hash +MODULE = Clownfish PACKAGE = Clownfish::Hash SV* _fetch(self, key) lucy_Hash *self; @@ -224,7 +224,7 @@ PPCODE: END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::Hash", ); $binding->exclude_method($_) for @hand_rolled; @@ -235,7 +235,7 @@ END_XS_CODE sub bind_host { my $xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::Host +MODULE = Clownfish PACKAGE = Clownfish::Host =for comment @@ -312,7 +312,7 @@ OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::Host", ); $binding->append_xs($xs_code); @@ -322,7 +322,7 @@ END_XS_CODE sub bind_lockfreeregistry { my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::LockFreeRegistry", ); Clownfish::CFC::Binding::Perl::Class->register($binding); @@ -330,7 +330,7 @@ sub bind_lockfreeregistry { sub bind_float32 { my $float32_xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::Float32 +MODULE = Clownfish PACKAGE = Clownfish::Float32 SV* new(either_sv, value) @@ -346,7 +346,7 @@ OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::Float32", ); $binding->append_xs($float32_xs_code); @@ -357,7 +357,7 @@ END_XS_CODE sub bind_float64 { my $float64_xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::Float64 +MODULE = Clownfish PACKAGE = Clownfish::Float64 SV* new(either_sv, value) @@ -373,7 +373,7 @@ OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::Float64", ); $binding->append_xs($float64_xs_code); @@ -483,7 +483,7 @@ END_DESCRIPTION $pod_spec->add_method( method => $_, alias => lc($_) ) for @exposed; my $xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::Obj +MODULE = Clownfish PACKAGE = Clownfish::Obj chy_bool_t is_a(self, class_name) @@ -498,7 +498,7 @@ OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::Obj", ); $binding->bind_method( alias => '_load', method => 'Load' ); @@ -520,7 +520,7 @@ sub bind_varray { ); my $xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::VArray +MODULE = Clownfish PACKAGE = Clownfish::VArray SV* shallow_copy(self) @@ -579,7 +579,7 @@ OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::VArray", ); $binding->exclude_method($_) for @hand_rolled; @@ -592,7 +592,7 @@ sub bind_vtable { my @hand_rolled = qw( Make_Obj ); my $xs_code = <<'END_XS_CODE'; -MODULE = Lucy PACKAGE = Clownfish::VTable +MODULE = Clownfish PACKAGE = Clownfish::VTable SV* _get_registry() @@ -636,7 +636,7 @@ OUTPUT: RETVAL END_XS_CODE my $binding = Clownfish::CFC::Binding::Perl::Class->new( - parcel => "Lucy", + parcel => "Clownfish", class_name => "Clownfish::VTable", ); $binding->exclude_method($_) for @hand_rolled;