Version in base suite: 17.0.2+8-1~deb11u1 Base version: openjdk-17_17.0.2+8-1~deb11u1 Target version: openjdk-17_17.0.3+7-1~deb11u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/o/openjdk-17/openjdk-17_17.0.2+8-1~deb11u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/o/openjdk-17/openjdk-17_17.0.3+7-1~deb11u1.dsc .jcheck/conf | 1 debian/changelog | 31 debian/patches/default-jvm-cfg.diff | 4 debian/patches/hotspot-mips-align.diff | 2 debian/patches/jaw-optional.diff | 2 debian/patches/jdk-getAccessibleValue.diff | 6 debian/patches/jtreg-location.diff | 8 debian/patches/libpcsclite-dlopen.diff | 4 debian/patches/m68k-support.diff | 10 debian/patches/multiple-pkcs11-library-init.diff | 4 debian/patches/reproducible-build-user.diff | 14 debian/patches/reproducible-properties-timestamp.diff | 4 debian/patches/riscv64.diff | 2 debian/patches/series | 1 debian/patches/system-pcsclite.diff | 2 debian/rules | 8 doc/building.html | 2 doc/building.md | 2 doc/testing.html | 4 doc/testing.md | 10 make/Main.gmk | 10 make/RunTests.gmk | 23 make/ToolsJdk.gmk | 2 make/autoconf/basic.m4 | 7 make/autoconf/jdk-options.m4 | 17 make/autoconf/jdk-version.m4 | 11 make/autoconf/spec.gmk.in | 2 make/autoconf/toolchain.m4 | 4 make/autoconf/toolchain_microsoft.m4 | 19 make/common/ZipArchive.gmk | 26 make/conf/version-numbers.conf | 4 make/data/currency/CurrencyData.properties | 4 make/devkit/createJMHBundle.sh | 2 make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java | 231 make/modules/java.base/Gendata.gmk | 6 make/modules/jdk.javadoc/Gendata.gmk | 4 make/test/JtregNativeHotspot.gmk | 4 src/demo/share/jfc/SwingSet2/TableDemo.java | 9 src/hotspot/cpu/aarch64/aarch64.ad | 40 src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 2 src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp | 2 src/hotspot/cpu/aarch64/globals_aarch64.hpp | 10 src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp | 38 src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp | 7 src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp | 48 src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp | 206 src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp | 4 src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp | 8 src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp | 5 src/hotspot/cpu/aarch64/vm_version_aarch64.cpp | 35 src/hotspot/cpu/aarch64/vm_version_aarch64.hpp | 7 src/hotspot/cpu/arm/assembler_arm_32.cpp | 2 src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp | 7 src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp | 4 src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp | 2 src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp | 4 src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp | 44 src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp | 10 src/hotspot/cpu/ppc/frame_ppc.cpp | 56 src/hotspot/cpu/ppc/matcher_ppc.hpp | 2 src/hotspot/cpu/ppc/ppc.ad | 40 src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp | 4 src/hotspot/cpu/s390/frame_s390.cpp | 60 src/hotspot/cpu/x86/assembler_x86.cpp | 2 src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp | 8 src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 25 src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp | 16 src/hotspot/cpu/x86/nativeInst_x86.cpp | 6 src/hotspot/cpu/x86/nativeInst_x86.hpp | 6 src/hotspot/cpu/x86/vmreg_x86.hpp | 8 src/hotspot/cpu/x86/x86.ad | 15 src/hotspot/cpu/x86/x86_32.ad | 41 src/hotspot/cpu/zero/globals_zero.hpp | 3 src/hotspot/cpu/zero/vm_version_zero.cpp | 4 src/hotspot/cpu/zero/zeroInterpreter_zero.cpp | 8 src/hotspot/os/bsd/decoder_machO.cpp | 6 src/hotspot/os/bsd/os_bsd.cpp | 44 src/hotspot/os/linux/cgroupSubsystem_linux.cpp | 118 src/hotspot/os/linux/cgroupSubsystem_linux.hpp | 14 src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp | 42 src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp | 10 src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 58 src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 5 src/hotspot/os/linux/osContainer_linux.cpp | 12 src/hotspot/os/linux/osContainer_linux.hpp | 4 src/hotspot/os/linux/os_linux.cpp | 19 src/hotspot/os/posix/signals_posix.cpp | 18 src/hotspot/os/windows/os_windows.cpp | 5 src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp | 15 src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp | 11 src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp | 13 src/hotspot/share/c1/c1_IR.cpp | 9 src/hotspot/share/c1/c1_IR.hpp | 10 src/hotspot/share/c1/c1_Instruction.cpp | 5 src/hotspot/share/c1/c1_LIRAssembler.cpp | 3 src/hotspot/share/c1/c1_LIRGenerator.cpp | 8 src/hotspot/share/c1/c1_MacroAssembler.hpp | 4 src/hotspot/share/cds/dynamicArchive.cpp | 1 src/hotspot/share/cds/metaspaceShared.cpp | 14 src/hotspot/share/classfile/altHashing.cpp | 33 src/hotspot/share/classfile/altHashing.hpp | 4 src/hotspot/share/classfile/classLoaderData.cpp | 5 src/hotspot/share/classfile/symbolTable.cpp | 7 src/hotspot/share/classfile/systemDictionaryShared.cpp | 22 src/hotspot/share/classfile/systemDictionaryShared.hpp | 1 src/hotspot/share/classfile/verifier.cpp | 5 src/hotspot/share/code/codeCache.cpp | 8 src/hotspot/share/compiler/compileBroker.cpp | 1 src/hotspot/share/compiler/compilerDirectives.cpp | 12 src/hotspot/share/gc/g1/g1RemSet.cpp | 4 src/hotspot/share/gc/shared/suspendibleThreadSet.cpp | 26 src/hotspot/share/gc/shared/suspendibleThreadSet.hpp | 8 src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp | 2 src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp | 4 src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp | 65 src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp | 1 src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp | 1 src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp | 7 src/hotspot/share/interpreter/bytecodeUtils.cpp | 1 src/hotspot/share/interpreter/bytecodes.cpp | 4 src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp | 73 src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp | 3 src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp | 6 src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp | 2 src/hotspot/share/jvmci/jvmciCompilerToVM.cpp | 33 src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 4 src/hotspot/share/memory/allocation.hpp | 1 src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp | 3 src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp | 1 src/hotspot/share/memory/metaspace/metaspaceReporter.cpp | 18 src/hotspot/share/memory/metaspace/metaspaceReporter.hpp | 4 src/hotspot/share/oops/instanceKlass.cpp | 48 src/hotspot/share/oops/instanceKlass.hpp | 18 src/hotspot/share/oops/klass.cpp | 2 src/hotspot/share/oops/klass.hpp | 11 src/hotspot/share/oops/method.cpp | 13 src/hotspot/share/oops/methodData.hpp | 8 src/hotspot/share/opto/buildOopMap.cpp | 18 src/hotspot/share/opto/cfgnode.cpp | 28 src/hotspot/share/opto/library_call.cpp | 4 src/hotspot/share/opto/loopPredicate.cpp | 46 src/hotspot/share/opto/loopTransform.cpp | 68 src/hotspot/share/opto/loopnode.cpp | 8 src/hotspot/share/opto/loopnode.hpp | 22 src/hotspot/share/opto/loopopts.cpp | 10 src/hotspot/share/opto/macro.cpp | 14 src/hotspot/share/opto/mulnode.cpp | 13 src/hotspot/share/opto/opaquenode.cpp | 19 src/hotspot/share/opto/opaquenode.hpp | 6 src/hotspot/share/opto/output.cpp | 10 src/hotspot/share/prims/jvmtiEnvBase.cpp | 2 src/hotspot/share/prims/jvmtiRedefineClasses.cpp | 3 src/hotspot/share/prims/vectorSupport.cpp | 1 src/hotspot/share/prims/whitebox.cpp | 2 src/hotspot/share/runtime/deoptimization.cpp | 10 src/hotspot/share/runtime/deoptimization.hpp | 12 src/hotspot/share/runtime/objectMonitor.hpp | 2 src/hotspot/share/runtime/os.cpp | 2 src/hotspot/share/services/attachListener.cpp | 3 src/hotspot/share/utilities/accessFlags.hpp | 5 src/hotspot/share/utilities/resourceHash.hpp | 26 src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java | 12 src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java | 8 src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java | 4 src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java | 27 src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java | 29 src/java.base/macosx/classes/apple/security/KeychainStore.java | 192 src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m | 63 src/java.base/share/classes/java/io/File.java | 10 src/java.base/share/classes/java/io/FileSystem.java | 7 src/java.base/share/classes/java/lang/String.java | 11 src/java.base/share/classes/java/math/BigInteger.java | 4 src/java.base/share/classes/java/util/random/package-info.java | 12 src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java | 6 src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java | 2 src/java.base/share/classes/jdk/internal/platform/Metrics.java | 21 src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java | 2 src/java.base/share/classes/sun/launcher/LauncherHelper.java | 15 src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java | 13 src/java.base/share/classes/sun/security/provider/DSA.java | 5 src/java.base/share/classes/sun/security/provider/certpath/OCSP.java | 18 src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java | 9 src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java | 72 src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java | 23 src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java | 17 src/java.base/share/classes/sun/security/tools/keytool/Main.java | 5 src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java | 17 src/java.base/share/classes/sun/security/util/DerValue.java | 28 src/java.base/share/classes/sun/security/util/ObjectIdentifier.java | 10 src/java.base/share/classes/sun/security/util/SignatureUtil.java | 28 src/java.base/share/classes/sun/security/x509/AlgorithmId.java | 2 src/java.base/share/classes/sun/security/x509/X509CRLImpl.java | 10 src/java.base/share/classes/sun/security/x509/X509CertImpl.java | 8 src/java.base/share/classes/sun/util/resources/CurrencyNames.properties | 4 src/java.base/unix/classes/java/io/UnixFileSystem.java | 7 src/java.base/unix/classes/sun/net/www/content-types.properties | 88 src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c | 4 src/java.base/windows/classes/java/io/WinNTFileSystem.java | 48 src/java.base/windows/classes/sun/net/www/content-types.properties | 86 src/java.base/windows/native/libnet/DefaultProxySelector.c | 18 src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java | 10 src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java | 25 src/java.desktop/share/classes/javax/swing/JLabel.java | 22 src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java | 2 src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java | 71 src/java.desktop/share/native/liblcms/LCMS.c | 12 src/java.desktop/share/native/liblcms/cmsalpha.c | 675 + src/java.desktop/share/native/liblcms/cmscam02.c | 515 src/java.desktop/share/native/liblcms/cmscgats.c | 2815 ++++ src/java.desktop/share/native/liblcms/cmscnvrt.c | 1243 ++ src/java.desktop/share/native/liblcms/cmserr.c | 692 + src/java.desktop/share/native/liblcms/cmsgamma.c | 1517 ++ src/java.desktop/share/native/liblcms/cmsgmt.c | 619 + src/java.desktop/share/native/liblcms/cmshalf.c | 564 src/java.desktop/share/native/liblcms/cmsintrp.c | 1345 ++ src/java.desktop/share/native/liblcms/cmsio0.c | 1979 +++ src/java.desktop/share/native/liblcms/cmsio1.c | 1057 + src/java.desktop/share/native/liblcms/cmslut.c | 1866 +++ src/java.desktop/share/native/liblcms/cmsmd5.c | 342 src/java.desktop/share/native/liblcms/cmsmtrx.c | 205 src/java.desktop/share/native/liblcms/cmsnamed.c | 1009 + src/java.desktop/share/native/liblcms/cmsopt.c | 2005 +++ src/java.desktop/share/native/liblcms/cmspack.c | 3462 ++++++ src/java.desktop/share/native/liblcms/cmspcs.c | 969 + src/java.desktop/share/native/liblcms/cmsplugin.c | 1020 + src/java.desktop/share/native/liblcms/cmsps2.c | 1647 ++ src/java.desktop/share/native/liblcms/cmssamp.c | 576 + src/java.desktop/share/native/liblcms/cmssm.c | 765 + src/java.desktop/share/native/liblcms/cmstypes.c | 5656 ++++++++++ src/java.desktop/share/native/liblcms/cmsvirt.c | 1246 ++ src/java.desktop/share/native/liblcms/cmswtpnt.c | 379 src/java.desktop/share/native/liblcms/cmsxform.c | 1394 ++ src/java.desktop/share/native/liblcms/lcms2.h | 1951 +++ src/java.desktop/share/native/liblcms/lcms2_internal.h | 1151 ++ src/java.desktop/share/native/liblcms/lcms2_plugin.h | 709 + src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c | 20 src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c | 50 src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp | 69 src/java.desktop/windows/native/libawt/windows/awt_Component.cpp | 26 src/java.naming/share/classes/com/sun/jndi/ldap/LdapClientFactory.java | 16 src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java | 67 src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java | 271 src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java | 109 src/java.naming/share/classes/com/sun/jndi/ldap/pool/PooledConnectionFactory.java | 9 src/java.naming/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java | 19 src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java | 279 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/Init.java | 10 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java | 6 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerPhysical.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/exceptions/XMLSecurityRuntimeException.java | 181 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java | 12 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java | 3 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_en.properties | 3 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/signature/XMLSignatureInput.java | 15 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14N.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14NExclusive.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformEnvelopedSignature.java | 8 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java | 15 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/Base64.java | 1 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/DOMNamespaceContext.java | 6 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/RFC2253Parser.java | 112 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/XMLUtils.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolver.java | 13 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverContext.java | 1 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java | 5 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java | 45 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/ApacheCanonicalizer.java | 3 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java | 2 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMTransform.java | 2 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java | 2 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Policy.java | 25 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Utils.java | 2 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/XMLDSigRI.java | 2 src/java.xml.crypto/share/legal/santuario.md | 2 src/java.xml/share/classes/com/sun/java_cup/internal/runtime/lr_parser.java | 62 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java | 480 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java | 15 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java | 47 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java | 12 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java | 14 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java | 25 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java | 15 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java | 9 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java | 10 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java | 14 src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java | 49 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java | 29 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java | 71 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/dom3/DOM3TreeWalker.java | 20 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/dom3/LSSerializerImpl.java | 13 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/utils/SystemIDResolver.java | 7 src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java | 14 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/XPath.java | 89 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Lexer.java | 161 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Token.java | 76 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/XPathParser.java | 319 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java | 17 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java | 20 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java | 6 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathResultImpl.java | 8 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java | 15 src/java.xml/share/classes/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java | 15 src/java.xml/share/classes/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java | 14 src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java | 17 src/java.xml/share/classes/jdk/xml/internal/JdkProperty.java | 29 src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java | 48 src/java.xml/share/classes/jdk/xml/internal/XMLLimitAnalyzer.java | 248 src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java | 633 + src/java.xml/share/classes/module-info.java | 97 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java | 2 src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java | 1 src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java | 13 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java | 5 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java | 48 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ConfigurationPath.java | 75 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java | 12 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java | 67 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java | 11 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java | 6 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java | 42 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ScriptEngine.java | 153 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java | 210 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java | 6 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java | 81 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java | 557 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java | 13 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java | 13 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java | 150 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java | 41 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java | 7 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java | 355 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java | 25 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java | 2 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JansiSupport.java | 7 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JnaSupport.java | 7 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java | 142 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java | 12 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java | 20 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java | 94 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java | 262 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java | 119 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java | 75 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java | 4 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java | 2 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java | 9 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java | 14 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java | 6 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java | 2 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java | 11 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java | 109 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-basic.caps | 41 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode-256color.caps | 44 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode.caps | 44 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt.caps | 43 src/jdk.internal.le/share/legal/jline.md | 2 src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaSupportImpl.java | 27 src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java | 36 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java | 12 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java | 22 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java | 4 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMemoryAccessProviderImpl.java | 8 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotNmethod.java | 21 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotObjectConstantImpl.java | 8 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java | 2 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/MemoryAccessProvider.java | 2 src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java | 23 src/jdk.jartool/share/classes/sun/tools/jar/Main.java | 64 src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties | 10 src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java | 4 src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java | 6 src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java | 1 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java | 6 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java | 1 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java | 1 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java | 1 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java | 3 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java | 27 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java | 2 src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java | 2 src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java | 6 src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java | 23 src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java | 66 src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties | 7 src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java | 8 src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java | 21 src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java | 71 src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java | 297 src/jdk.random/share/classes/module-info.java | 3 test/hotspot/gtest/gc/shared/test_ptrQueueBufferAllocator.cpp | 8 test/hotspot/gtest/gtestMain.cpp | 61 test/hotspot/gtest/runtime/test_os.cpp | 2 test/hotspot/gtest/unittest.hpp | 11 test/hotspot/gtest/utilities/test_resourceHash.cpp | 24 test/hotspot/jtreg/TEST.groups | 47 test/hotspot/jtreg/compiler/c1/Test8271202.java | 67 test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathology.jasm | 76 test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathologyMain.java | 40 test/hotspot/jtreg/compiler/c2/TestDeadDataLoopCmoveIdentity.java | 82 test/hotspot/jtreg/compiler/c2/TestSqrt.java | 54 test/hotspot/jtreg/compiler/c2/irTests/TestScheduleSmallMethod.java | 58 test/hotspot/jtreg/compiler/ciReplay/TestVMNoCompLevel.java | 3 test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java | 2 test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderData.java | 22 test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderTest.java | 5 test/hotspot/jtreg/compiler/loopopts/TestDeadPostLoopBecausePredicate.java | 79 test/hotspot/jtreg/compiler/loopopts/TestEliminateNullCheckWithSplitIf.java | 102 test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java | 66 test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64.java | 218 test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java | 98 test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitNoneAArch64.java | 82 test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java | 5 test/hotspot/jtreg/compiler/vectorapi/TestLongVectorNeg.java | 51 test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java | 5 test/hotspot/jtreg/containers/docker/DockerBasicTest.java | 2 test/hotspot/jtreg/containers/docker/TestCPUAwareness.java | 2 test/hotspot/jtreg/containers/docker/TestCPUSets.java | 2 test/hotspot/jtreg/containers/docker/TestJFREvents.java | 2 test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java | 2 test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java | 2 test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java | 2 test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java | 2 test/hotspot/jtreg/containers/docker/TestMisc.java | 6 test/hotspot/jtreg/containers/docker/TestPids.java | 162 test/hotspot/jtreg/gc/g1/TestMixedGCLiveThreshold.java | 42 test/hotspot/jtreg/gc/stress/gclocker/TestGCLockerWithSerial.java | 2 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationAgeThreshold.java | 18 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationFullGC.java | 18 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationInterned.java | 5 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationPrintOptions.java | 18 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTableResize.java | 18 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java | 7 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationYoungGC.java | 18 test/hotspot/jtreg/runtime/BootstrapMethod/BSMCalledTwice.java | 12 test/hotspot/jtreg/runtime/CommandLine/CompilerConfigFileWarning.java | 5 test/hotspot/jtreg/runtime/CommandLine/ConfigFileWarning.java | 3 test/hotspot/jtreg/runtime/CommandLine/ObsoleteFlagErrorMessage.java | 3 test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethods.java | 3 test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java | 8 test/hotspot/jtreg/runtime/CommandLine/TestHexArguments.java | 3 test/hotspot/jtreg/runtime/CommandLine/TestVMOptions.java | 5 test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java | 6 test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java | 23 test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestArena.java | 4 test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestManyArenasManyThreads.java | 6 test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestWithThreads.java | 8 test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocator.java | 34 test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocatorThread.java | 9 test/hotspot/jtreg/runtime/Safepoint/TestAbortOnVMOperationTimeout.java | 18 test/hotspot/jtreg/runtime/Thread/TestSpinPause.java | 87 test/hotspot/jtreg/runtime/cds/appcds/LambdaContainsOldInf.java | 78 test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java | 81 test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java | 30 test/hotspot/jtreg/runtime/cds/appcds/test-classes/LambdaContainsOldInfApp.java | 34 test/hotspot/jtreg/runtime/cds/appcds/test-classes/OldProvider.jasm | 29 test/hotspot/jtreg/runtime/jsig/Testjsig.java | 2 test/hotspot/jtreg/runtime/modules/LoadUnloadModuleStress.java | 2 test/hotspot/jtreg/runtime/verifier/PutfieldProtectedTest.java | 45 test/hotspot/jtreg/runtime/verifier/putfieldProtected.jasm | 56 test/hotspot/jtreg/serviceability/dcmd/framework/TestProcessLauncher.java | 2 test/hotspot/jtreg/vmTestbase/gc/gctests/PhantomReference/phantom001/phantom001.java | 2 test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft001/soft001.java | 5 test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft003/soft003.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft004/soft004.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft005/soft005.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak001/weak001.java | 5 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak003/weak003.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak004/weak004.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak005/weak005.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak006/weak006.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak007/weak007.java | 4 test/hotspot/jtreg/vmTestbase/jit/t/t105/t105.java | 9 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy001/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy002/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy003/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy004/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy005/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy006/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy007/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy008/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy009/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy010/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy011/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy012/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy013/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy014/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy015/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/jvmti/StopThread/stopthrd007.java | 8 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree001/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree002/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree003/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree004/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree005/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree006/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree007/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree008/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree009/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree010/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree011/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree012/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain001/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain002/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain003/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain004/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain005/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain006/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain007/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain008/TEST.properties | 1 test/jdk/ProblemList.txt | 1 test/jdk/TEST.ROOT | 2 test/jdk/TEST.groups | 13 test/jdk/build/AbsPathsInImage.java | 12 test/jdk/com/sun/jdi/TestScaffold.java | 11 test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java | 139 test/jdk/java/awt/FontClass/DrawStringWithInfiniteXform.java | 10 test/jdk/java/awt/Window/WindowResizingOnDPIChanging/WindowResizingOnMovingToAnotherDisplay.java | 2 test/jdk/java/io/File/GetXSpace.java | 7 test/jdk/java/io/File/LastModifiedTest.java | 60 test/jdk/java/lang/ProcessBuilder/Basic.java | 155 test/jdk/java/lang/ProcessBuilder/exeBasicSleep.c | 54 test/jdk/java/math/BigInteger/BitLengthOverflow.java | 35 test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java | 3 test/jdk/java/nio/file/Files/probeContentType/Basic.java | 76 test/jdk/java/security/testlibrary/CertificateBuilder.java | 42 test/jdk/java/security/testlibrary/SimpleOCSPServer.java | 14 test/jdk/java/text/Format/DateFormat/DateFormatTest.java | 6 test/jdk/java/text/Format/DateFormat/NonGregorianFormatTest.java | 12 test/jdk/java/util/Currency/ValidateISO4217.java | 6 test/jdk/java/util/Currency/tablea1.txt | 6 test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java | 20 test/jdk/javax/swing/JFileChooser/FileSystemView/ShellFolderStackOverflow.java | 39 test/jdk/javax/swing/UIDefaults/6302464/bug6302464.java | 14 test/jdk/javax/swing/text/FlowView/6318524/bug6318524.java | 194 test/jdk/javax/swing/text/ParagraphView/6364882/bug6364882.java | 252 test/jdk/javax/xml/jaxp/XPath/InvalidXPath.java | 53 test/jdk/jdk/editpad/EditPadTest.java | 1 test/jdk/jdk/internal/jline/AbstractWindowsTerminalTest.java | 7 test/jdk/jdk/internal/jline/KeyConversionTest.java | 9 test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java | 2 test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java | 2 test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java | 2 test/jdk/jdk/internal/platform/docker/TestPidsLimit.java | 132 test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java | 2 test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java | 2 test/jdk/sun/java2d/cmm/ColorConvertOp/MTPerLineTransformValidation.java | 146 test/jdk/sun/java2d/cmm/ColorConvertOp/MTTransformValidation.java | 143 test/jdk/sun/security/lib/cacerts/VerifyCACerts.java | 4 test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java | 6 test/jdk/sun/security/ssl/SSLSocketImpl/ClientSocketCloseHang.java | 134 test/jdk/sun/text/resources/LocaleData | 3 test/jdk/sun/text/resources/LocaleDataTest.java | 4 test/jdk/tools/jar/ContentOrder.java | 214 test/jdk/tools/jar/ReproducibleJar.java | 291 test/jdk/tools/jimage/JImageNonAsciiNameTest.java | 98 test/jdk/tools/jmod/JmodTest.java | 79 test/langtools/jdk/jshell/CommandCompletionTest.java | 25 test/langtools/tools/javac/generics/diamond/protectedConstructor/ProtectedConstructorTest.java | 50 test/langtools/tools/javac/generics/diamond/protectedConstructor/pkg/Bar.java | 29 test/lib/jdk/test/lib/SecurityTools.java | 61 test/lib/jdk/test/lib/containers/docker/Common.java | 6 test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java | 75 test/lib/sun/hotspot/WhiteBox.java | 2 test/micro/org/openjdk/bench/java/lang/StringEncode.java | 126 test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWait.java | 52 test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java | 204 test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitSharedCounter.java | 88 569 files changed, 52486 insertions(+), 3951 deletions(-) diff -Nru openjdk-17-17.0.2+8/.jcheck/conf openjdk-17-17.0.3+7/.jcheck/conf --- openjdk-17-17.0.2+8/.jcheck/conf 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/.jcheck/conf 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,7 @@ [general] project=jdk-updates jbs=JDK +version=17.0.3 [checks] error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists diff -Nru openjdk-17-17.0.2+8/debian/changelog openjdk-17-17.0.3+7/debian/changelog --- openjdk-17-17.0.2+8/debian/changelog 2022-01-21 16:38:47.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/changelog 2022-05-02 22:04:41.000000000 +0000 @@ -1,8 +1,35 @@ -openjdk-17 (17.0.2+8-1~deb11u1) bullseye-security; urgency=medium +openjdk-17 (17.0.3+7-1~deb11u1) bullseye-security; urgency=medium * Rebuild for bullseye - -- Moritz Muehlenhoff Fri, 21 Jan 2022 17:38:47 +0100 + -- Moritz Muehlenhoff Tue, 03 May 2022 00:04:41 +0200 + +openjdk-17 (17.0.3+7-1) unstable; urgency=high + + * OpenJDK 17.0.3+7 (release). + * Security fixes + - JDK-8269938: Enhance XML processing passes redux. + - JDK-8270504, CVE-2022-21426: Better XPath expression handling. + - JDK-8272255: Completely handle MIDI files. + - JDK-8272261: Improve JFR recording file processing. + - JDK-8272588: Enhanced recording parsing. + - JDK-8272594: Better record of recordings. + - JDK-8274221: More definite BER encodings. + - JDK-8275082, JDK-8278008, CVE-2022-21476: Update XML Security for Java + to 2.3.0. + - JDK-8275151, CVE-2022-21443: Improved Object Identification. + - JDK-8277227: Better identification of OIDs. + - JDK-8277233, CVE-2022-21449: Improve ECDSA signature support. + - JDK-8277672, CVE-2022-21434: Better invocation handler handling. + - JDK-8278356: Improve file creation. + - JDK-8278449: Improve keychain support. + - JDK-8278798: Improve supported intrinsic. + - JDK-8278805: Enhance BMP image loading. + - JDK-8278972, CVE-2022-21496: Improve URL supports. + - JDK-8281388: Change wrapping of EncryptedPrivateKeyInfo. + * Refresh patches. + + -- Matthias Klose Mon, 02 May 2022 20:04:05 +0200 openjdk-17 (17.0.2+8-1) unstable; urgency=high diff -Nru openjdk-17-17.0.2+8/debian/patches/default-jvm-cfg.diff openjdk-17-17.0.3+7/debian/patches/default-jvm-cfg.diff --- openjdk-17-17.0.2+8/debian/patches/default-jvm-cfg.diff 2021-04-08 07:39:27.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/default-jvm-cfg.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1,6 +1,6 @@ --- a/src/java.base/share/native/libjli/java.c +++ b/src/java.base/share/native/libjli/java.c -@@ -2077,7 +2077,7 @@ jint +@@ -2079,7 +2079,7 @@ jint ReadKnownVMs(const char *jvmCfgName, jboolean speculative) { FILE *jvmCfg; @@ -9,7 +9,7 @@ int cnt = 0; int lineno = 0; jlong start = 0, end = 0; -@@ -2092,6 +2092,11 @@ ReadKnownVMs(const char *jvmCfgName, jbo +@@ -2094,6 +2094,11 @@ ReadKnownVMs(const char *jvmCfgName, jbo jvmCfg = fopen(jvmCfgName, "r"); if (jvmCfg == NULL) { diff -Nru openjdk-17-17.0.2+8/debian/patches/hotspot-mips-align.diff openjdk-17-17.0.3+7/debian/patches/hotspot-mips-align.diff --- openjdk-17-17.0.2+8/debian/patches/hotspot-mips-align.diff 2021-05-27 09:28:46.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/hotspot-mips-align.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1,6 +1,6 @@ --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp -@@ -414,7 +414,7 @@ int ZeroInterpreter::native_entry(Method +@@ -418,7 +418,7 @@ int ZeroInterpreter::native_entry(Method ThreadStateTransition::transition_from_java(thread, _thread_in_native); // Make the call diff -Nru openjdk-17-17.0.2+8/debian/patches/jaw-optional.diff openjdk-17-17.0.3+7/debian/patches/jaw-optional.diff --- openjdk-17-17.0.2+8/debian/patches/jaw-optional.diff 2020-11-07 11:16:22.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/jaw-optional.diff 2022-05-02 18:04:05.000000000 +0000 @@ -6,7 +6,7 @@ --- a/src/java.desktop/share/classes/java/awt/Toolkit.java +++ b/src/java.desktop/share/classes/java/awt/Toolkit.java -@@ -600,7 +600,11 @@ public abstract class Toolkit { +@@ -602,7 +602,11 @@ public abstract class Toolkit { toolkit = new HeadlessToolkit(toolkit); } if (!GraphicsEnvironment.isHeadless()) { diff -Nru openjdk-17-17.0.2+8/debian/patches/jdk-getAccessibleValue.diff openjdk-17-17.0.3+7/debian/patches/jdk-getAccessibleValue.diff --- openjdk-17-17.0.2+8/debian/patches/jdk-getAccessibleValue.diff 2021-04-08 07:39:38.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/jdk-getAccessibleValue.diff 2022-05-02 18:04:05.000000000 +0000 @@ -193,7 +193,7 @@ --- a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java +++ b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java -@@ -6550,7 +6550,7 @@ final public class AccessBridge { +@@ -6555,7 +6555,7 @@ final public class AccessBridge { public AccessibleSelection getAccessibleSelection() { AccessibleContext ac = getCurrentAccessibleContext(); if (ac != null && isLeaf) { @@ -202,7 +202,7 @@ } else { return this; } -@@ -6565,7 +6565,7 @@ final public class AccessBridge { +@@ -6570,7 +6570,7 @@ final public class AccessBridge { public AccessibleText getAccessibleText() { AccessibleContext ac = getCurrentAccessibleContext(); if (ac != null) { @@ -211,7 +211,7 @@ } else { return null; } -@@ -6580,7 +6580,7 @@ final public class AccessBridge { +@@ -6585,7 +6585,7 @@ final public class AccessBridge { public AccessibleValue getAccessibleValue() { AccessibleContext ac = getCurrentAccessibleContext(); if (ac != null) { diff -Nru openjdk-17-17.0.2+8/debian/patches/jtreg-location.diff openjdk-17-17.0.3+7/debian/patches/jtreg-location.diff --- openjdk-17-17.0.2+8/debian/patches/jtreg-location.diff 2021-05-27 09:29:01.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/jtreg-location.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1,6 +1,6 @@ --- a/make/autoconf/toolchain.m4 +++ b/make/autoconf/toolchain.m4 -@@ -1022,7 +1022,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], +@@ -1029,7 +1029,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], AC_MSG_ERROR([jtreg home directory from --with-jtreg=$with_jtreg does not exist]) fi @@ -9,7 +9,7 @@ AC_MSG_ERROR([jtreg home directory from --with-jtreg=$with_jtreg is not a valid jtreg home]) fi -@@ -1036,7 +1036,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], +@@ -1043,7 +1043,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], AC_MSG_WARN([Ignoring JT_HOME pointing to invalid directory: $JT_HOME]) JT_HOME= else @@ -18,7 +18,7 @@ AC_MSG_WARN([Ignoring JT_HOME which is not a valid jtreg home: $JT_HOME]) JT_HOME= else -@@ -1052,7 +1052,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], +@@ -1059,7 +1059,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], if test "x$JTREGEXE" != x; then # That's good, now try to derive JT_HOME JT_HOME=`(cd $($DIRNAME $JTREGEXE)/.. && pwd)` @@ -29,7 +29,7 @@ else --- a/make/RunTests.gmk +++ b/make/RunTests.gmk -@@ -837,7 +837,7 @@ define SetupRunJtregTestBody +@@ -846,7 +846,7 @@ define SetupRunJtregTestBody $1_COMMAND_LINE := \ $$(JAVA) $$($1_JTREG_LAUNCHER_OPTIONS) \ diff -Nru openjdk-17-17.0.2+8/debian/patches/libpcsclite-dlopen.diff openjdk-17-17.0.3+7/debian/patches/libpcsclite-dlopen.diff --- openjdk-17-17.0.2+8/debian/patches/libpcsclite-dlopen.diff 2021-02-25 09:49:28.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/libpcsclite-dlopen.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1,6 +1,6 @@ --- a/src/java.smartcardio/unix/classes/sun/security/smartcardio/PlatformPCSC.java +++ b/src/java.smartcardio/unix/classes/sun/security/smartcardio/PlatformPCSC.java -@@ -48,6 +48,7 @@ class PlatformPCSC { +@@ -46,6 +46,7 @@ class PlatformPCSC { private final static String PROP_NAME = "sun.security.smartcardio.library"; @@ -8,7 +8,7 @@ private final static String LIB1 = "/usr/$LIBISA/libpcsclite.so"; private final static String LIB2 = "/usr/local/$LIBISA/libpcsclite.so"; private final static String PCSC_FRAMEWORK = "/System/Library/Frameworks/PCSC.framework/Versions/Current/PCSC"; -@@ -100,40 +101,9 @@ class PlatformPCSC { +@@ -98,40 +99,9 @@ class PlatformPCSC { if (lib.length() != 0) { return lib; } diff -Nru openjdk-17-17.0.2+8/debian/patches/m68k-support.diff openjdk-17-17.0.3+7/debian/patches/m68k-support.diff --- openjdk-17-17.0.2+8/debian/patches/m68k-support.diff 2021-05-27 09:29:19.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/m68k-support.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1042,7 +1042,7 @@ $(eval $(call SetupExecute, gen_x11wrappers, \ --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp -@@ -238,7 +238,7 @@ template class CHeapObj ALL +@@ -239,7 +239,7 @@ template class CHeapObj ALL void operator delete(void* p) { FreeHeap(p); } void operator delete [] (void* p) { FreeHeap(p); } @@ -1051,7 +1051,7 @@ // Base class for objects allocated on the stack only. // Calling new or delete will result in fatal error. -@@ -249,7 +249,7 @@ class StackObj ALLOCATION_SUPER_CLASS_SP +@@ -250,7 +250,7 @@ class StackObj ALLOCATION_SUPER_CLASS_SP void* operator new [](size_t size) throw(); void operator delete(void* p); void operator delete [](void* p); @@ -1060,7 +1060,7 @@ // Base class for objects stored in Metaspace. // Calling delete will result in fatal error. -@@ -356,7 +356,7 @@ class MetaspaceObj { +@@ -372,7 +372,7 @@ class MetaspaceObj { // that should be read-only by default. See symbol.hpp for an example. This function // is used by the templates in metaspaceClosure.hpp static bool is_read_only_by_default() { return false; } @@ -1069,7 +1069,7 @@ // Base class for classes that constitute name spaces. -@@ -441,7 +441,7 @@ protected: +@@ -457,7 +457,7 @@ protected: void operator delete(void* p); void operator delete [](void* p); @@ -1090,7 +1090,7 @@ #endif // SHARE_OOPS_CONSTMETHOD_HPP --- a/src/hotspot/share/oops/oop.hpp +++ b/src/hotspot/share/oops/oop.hpp -@@ -316,6 +316,6 @@ class oopDesc { +@@ -315,6 +315,6 @@ class oopDesc { // Avoid include gc_globals.hpp in oop.inline.hpp DEBUG_ONLY(bool get_UseParallelGC();) DEBUG_ONLY(bool get_UseG1GC();) diff -Nru openjdk-17-17.0.2+8/debian/patches/multiple-pkcs11-library-init.diff openjdk-17-17.0.3+7/debian/patches/multiple-pkcs11-library-init.diff --- openjdk-17-17.0.2+8/debian/patches/multiple-pkcs11-library-init.diff 2021-05-27 09:28:57.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/multiple-pkcs11-library-init.diff 2022-05-02 18:04:05.000000000 +0000 @@ -15,7 +15,7 @@ // same as allowSingleThreadedModules but controlled via a system property // and applied to all providers. if set to false, no SunPKCS11 instances -@@ -986,6 +987,8 @@ final class Config { +@@ -1019,6 +1020,8 @@ final class Config { handleStartupErrors = ERR_IGNORE_LIB; } else if (val.equals("halt")) { handleStartupErrors = ERR_HALT; @@ -26,7 +26,7 @@ } --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java -@@ -177,26 +177,37 @@ public final class SunPKCS11 extends Aut +@@ -179,26 +179,37 @@ public final class SunPKCS11 extends Aut String nssLibraryDirectory = config.getNssLibraryDirectory(); String nssSecmodDirectory = config.getNssSecmodDirectory(); boolean nssOptimizeSpace = config.getNssOptimizeSpace(); diff -Nru openjdk-17-17.0.2+8/debian/patches/reproducible-build-user.diff openjdk-17-17.0.3+7/debian/patches/reproducible-build-user.diff --- openjdk-17-17.0.2+8/debian/patches/reproducible-build-user.diff 2021-04-08 07:40:21.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/reproducible-build-user.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -Description: Makes the build user invariant to improve the reproducibility (it appears in the interval VM version in libjvm.so) -Author: Emmanuel Bourg -Forwarded: no ---- a/make/autoconf/basic.m4 -+++ b/make/autoconf/basic.m4 -@@ -97,7 +97,7 @@ AC_DEFUN_ONCE([BASIC_SETUP_PATHS], - - # Setup username (for use in adhoc version strings etc) - # Outer [ ] to quote m4. -- [ USERNAME=`$ECHO "$USER" | $TR -d -c '[a-z][A-Z][0-9]'` ] -+ [ USERNAME="unknown" ] - AC_SUBST(USERNAME) - ]) - diff -Nru openjdk-17-17.0.2+8/debian/patches/reproducible-properties-timestamp.diff openjdk-17-17.0.3+7/debian/patches/reproducible-properties-timestamp.diff --- openjdk-17-17.0.2+8/debian/patches/reproducible-properties-timestamp.diff 2021-04-08 07:39:56.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/reproducible-properties-timestamp.diff 2022-05-02 18:04:05.000000000 +0000 @@ -3,7 +3,7 @@ Forwarded: no --- a/src/java.base/share/classes/java/util/Properties.java +++ b/src/java.base/share/classes/java/util/Properties.java -@@ -914,7 +914,7 @@ public class Properties extends Hashtabl +@@ -903,7 +903,7 @@ public class Properties extends Hashtabl if (comments != null) { writeComments(bw, comments); } @@ -12,7 +12,7 @@ bw.newLine(); synchronized (this) { for (Map.Entry e : entrySet()) { -@@ -1566,4 +1566,22 @@ public class Properties extends Hashtabl +@@ -1555,4 +1555,22 @@ public class Properties extends Hashtabl } this.map = map; } diff -Nru openjdk-17-17.0.2+8/debian/patches/riscv64.diff openjdk-17-17.0.3+7/debian/patches/riscv64.diff --- openjdk-17-17.0.2+8/debian/patches/riscv64.diff 2021-05-27 09:32:51.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/riscv64.diff 2022-05-02 18:04:05.000000000 +0000 @@ -58,7 +58,7 @@ exit $exitcode --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp -@@ -2506,6 +2506,8 @@ void os::get_summary_cpu_info(char* cpui +@@ -2539,6 +2539,8 @@ void os::get_summary_cpu_info(char* cpui strncpy(cpuinfo, "IA64", length); #elif defined(PPC) strncpy(cpuinfo, "PPC64", length); diff -Nru openjdk-17-17.0.2+8/debian/patches/series openjdk-17-17.0.3+7/debian/patches/series --- openjdk-17-17.0.2+8/debian/patches/series 2021-10-20 14:58:22.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/series 2022-05-02 18:04:05.000000000 +0000 @@ -32,7 +32,6 @@ reproducible-character-data.diff reproducible-module-info.diff reproducible-copyright-headers.diff -reproducible-build-user.diff riscv64.diff reproducible-build-jmod.diff mips.diff diff -Nru openjdk-17-17.0.2+8/debian/patches/system-pcsclite.diff openjdk-17-17.0.3+7/debian/patches/system-pcsclite.diff --- openjdk-17-17.0.2+8/debian/patches/system-pcsclite.diff 2021-05-27 09:28:42.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/system-pcsclite.diff 2022-05-02 18:04:05.000000000 +0000 @@ -70,7 +70,7 @@ --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in -@@ -764,6 +764,7 @@ TAR_SUPPORTS_TRANSFORM:=@TAR_SUPPORTS_TR +@@ -769,6 +769,7 @@ TAR_SUPPORTS_TRANSFORM:=@TAR_SUPPORTS_TR # Build setup USE_EXTERNAL_LIBJPEG:=@USE_EXTERNAL_LIBJPEG@ USE_EXTERNAL_LIBGIF:=@USE_EXTERNAL_LIBGIF@ diff -Nru openjdk-17-17.0.2+8/debian/rules openjdk-17-17.0.3+7/debian/rules --- openjdk-17-17.0.2+8/debian/rules 2022-01-20 16:13:47.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/rules 2022-05-02 18:04:05.000000000 +0000 @@ -1871,7 +1871,7 @@ is_release = yes #is_release = git_project = jdk17u -git_tag = jdk-17.0.2+8 +git_tag = jdk-17.0.3+7 package_version = $(subst jdk-,,$(git_tag)) package_version = $(shell echo $(PKGVERSION) | sed 's/-[^-][^-]*$$//') ifneq ($(is_release),yes) @@ -1895,8 +1895,6 @@ -type f -print -delete; \ rm -v -rf $$d/src/java.desktop/share/native/libsplashscreen/giflib; \ rm -v -rf $$d/src/java.desktop/share/native/libsplashscreen/libpng; \ - rm -v -f $$d/src/java.desktop/share/native/liblcms/cms*.c; \ - rm -v -f $$d/src/java.desktop/share/native/liblcms/lcms2*.h; \ rm -v -rf $$d/src/java.smartcardio/unix/native/libj2pcsc/MUSCLE; \ esac; \ mv $$d $(topdir); \ @@ -1907,6 +1905,10 @@ tar cfJ ../$(basename)_$(package_version).orig.tar.xz $(topdir); \ rm -rf $(topdir) +# keep these in the tarball, older lcms are not supported + rm -v -f $$d/src/java.desktop/share/native/liblcms/cms*.c; \ + rm -v -f $$d/src/java.desktop/share/native/liblcms/lcms2*.h; \ + rm -rf $(origdir) binary: binary-arch binary-indep diff -Nru openjdk-17-17.0.2+8/doc/building.html openjdk-17-17.0.3+7/doc/building.html --- openjdk-17-17.0.2+8/doc/building.html 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/doc/building.html 2022-04-19 19:55:43.000000000 +0000 @@ -506,7 +506,7 @@

Running Tests

Most of the JDK tests are using the JTReg test framework. Make sure that your configuration knows where to find your installation of JTReg. If this is not picked up automatically, use the --with-jtreg=<path to jtreg home> option to point to the JTReg framework. Note that this option should point to the JTReg home, i.e. the top directory, containing lib/jtreg.jar etc.

-

The Adoption Group provides recent builds of jtreg here. Download the latest .tar.gz file, unpack it, and point --with-jtreg to the jtreg directory that you just unpacked.

+

The Adoption Group provides recent builds of jtreg here. Download the latest .tar.gz file, unpack it, and point --with-jtreg to the jtreg directory that you just unpacked.

Building of Hotspot Gtest suite requires the source code of Google Test framework. The top directory, which contains both googletest and googlemock directories, should be specified via --with-gtest. The supported version of Google Test is 1.8.1, whose source code can be obtained:

  • by downloading and unpacking the source bundle from here
  • diff -Nru openjdk-17-17.0.2+8/doc/building.md openjdk-17-17.0.3+7/doc/building.md --- openjdk-17-17.0.2+8/doc/building.md 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/doc/building.md 2022-04-19 19:55:43.000000000 +0000 @@ -848,7 +848,7 @@ The [Adoption Group](https://wiki.openjdk.java.net/display/Adoption) provides recent builds of jtreg [here]( -https://ci.adoptopenjdk.net/view/Dependencies/job/jtreg/lastSuccessfulBuild/artifact). +https://ci.adoptopenjdk.net/view/Dependencies/job/dependency_pipeline/lastSuccessfulBuild/artifact/jtreg/). Download the latest `.tar.gz` file, unpack it, and point `--with-jtreg` to the `jtreg` directory that you just unpacked. diff -Nru openjdk-17-17.0.2+8/doc/testing.html openjdk-17-17.0.3+7/doc/testing.html --- openjdk-17-17.0.2+8/doc/testing.html 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/doc/testing.html 2022-04-19 19:55:43.000000000 +0000 @@ -193,7 +193,9 @@

    AOT_MODULES

    Generate AOT modules before testing for the specified module, or set of modules. If multiple modules are specified, they should be separated by space (or, to help avoid quoting issues, the special value %20).

    RETRY_COUNT

    -

    Retry failed tests up to a set number of times. Defaults to 0.

    +

    Retry failed tests up to a set number of times, until they pass. This allows to pass the tests with intermittent failures. Defaults to 0.

    +

    REPEAT_COUNT

    +

    Repeat the tests up to a set number of times, stopping at first failure. This helps to reproduce intermittent test failures. Defaults to 0.

    Gtest keywords

    REPEAT

    The number of times to repeat the tests (--gtest_repeat).

    diff -Nru openjdk-17-17.0.2+8/doc/testing.md openjdk-17-17.0.3+7/doc/testing.md --- openjdk-17-17.0.2+8/doc/testing.md 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/doc/testing.md 2022-04-19 19:55:43.000000000 +0000 @@ -419,7 +419,15 @@ #### RETRY_COUNT -Retry failed tests up to a set number of times. Defaults to 0. +Retry failed tests up to a set number of times, until they pass. +This allows to pass the tests with intermittent failures. +Defaults to 0. + +#### REPEAT_COUNT + +Repeat the tests up to a set number of times, stopping at first failure. +This helps to reproduce intermittent test failures. +Defaults to 0. ### Gtest keywords diff -Nru openjdk-17-17.0.2+8/make/Main.gmk openjdk-17-17.0.3+7/make/Main.gmk --- openjdk-17-17.0.2+8/make/Main.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/Main.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -324,7 +324,7 @@ # aren't built until after libjava and libjvm are available to link to. $(eval $(call SetupTarget, demos-jdk, \ MAKEFILE := CompileDemos, \ - DEPS := java.base-libs exploded-image, \ + DEPS := java.base-libs exploded-image buildtools-jdk, \ )) $(eval $(call SetupTarget, test-image-demos-jdk, \ @@ -383,12 +383,12 @@ $(eval $(call SetupTarget, zip-security, \ MAKEFILE := ZipSecurity, \ - DEPS := java.base-java java.security.jgss-java java.security.jgss-libs, \ + DEPS := buildtools-jdk java.base-java java.security.jgss-java java.security.jgss-libs, \ )) $(eval $(call SetupTarget, zip-source, \ MAKEFILE := ZipSource, \ - DEPS := gensrc, \ + DEPS := buildtools-jdk gensrc, \ )) $(eval $(call SetupTarget, jrtfs-jar, \ @@ -508,13 +508,13 @@ $(eval $(call SetupTarget, docs-zip, \ MAKEFILE := Docs, \ TARGET := docs-zip, \ - DEPS := docs-jdk, \ + DEPS := docs-jdk buildtools-jdk, \ )) $(eval $(call SetupTarget, docs-specs-zip, \ MAKEFILE := Docs, \ TARGET := docs-specs-zip, \ - DEPS := docs-jdk-specs, \ + DEPS := docs-jdk-specs buildtools-jdk, \ )) $(eval $(call SetupTarget, update-build-docs, \ diff -Nru openjdk-17-17.0.2+8/make/RunTests.gmk openjdk-17-17.0.3+7/make/RunTests.gmk --- openjdk-17-17.0.2+8/make/RunTests.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/RunTests.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -200,7 +200,7 @@ $(eval $(call ParseKeywordVariable, JTREG, \ SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \ TEST_MODE ASSERT VERBOSE RETAIN MAX_MEM RUN_PROBLEM_LISTS \ - RETRY_COUNT MAX_OUTPUT, \ + RETRY_COUNT REPEAT_COUNT MAX_OUTPUT, \ STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \ EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS, \ )) @@ -744,6 +744,15 @@ JTREG_RETAIN ?= fail,error JTREG_RUN_PROBLEM_LISTS ?= false JTREG_RETRY_COUNT ?= 0 + JTREG_REPEAT_COUNT ?= 0 + + ifneq ($$(JTREG_RETRY_COUNT), 0) + ifneq ($$(JTREG_REPEAT_COUNT), 0) + $$(info Error: Cannot use both JTREG_RETRY_COUNT and JTREG_REPEAT_COUNT together.) + $$(info Please choose one or the other.) + $$(error Cannot continue) + endif + endif ifneq ($$(JTREG_LAUNCHER_OPTIONS), ) $1_JTREG_LAUNCHER_OPTIONS += $$(JTREG_LAUNCHER_OPTIONS) @@ -866,6 +875,18 @@ done endif + ifneq ($$(JTREG_REPEAT_COUNT), 0) + $1_COMMAND_LINE := \ + for i in {1..$$(JTREG_REPEAT_COUNT)}; do \ + $$(PRINTF) "\nRepeating Jtreg run: $$$$i out of $$(JTREG_REPEAT_COUNT)\n"; \ + $$($1_COMMAND_LINE); \ + if [ "`$$(CAT) $$($1_EXITCODE)`" != "0" ]; then \ + $$(PRINTF) "\nFailures detected, no more repeats.\n"; \ + break; \ + fi; \ + done + endif + run-test-$1: pre-run-test clean-workdir-$1 $$(call LogWarn) $$(call LogWarn, Running test '$$($1_TEST)') diff -Nru openjdk-17-17.0.2+8/make/ToolsJdk.gmk openjdk-17-17.0.3+7/make/ToolsJdk.gmk --- openjdk-17-17.0.2+8/make/ToolsJdk.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/ToolsJdk.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -80,6 +80,8 @@ TOOL_GENERATEEMOJIDATA = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ build.tools.generateemojidata.GenerateEmojiData +TOOL_MAKEZIPREPRODUCIBLE = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ + build.tools.makezipreproducible.MakeZipReproducible # TODO: There are references to the jdwpgen.jar in jdk/make/netbeans/jdwpgen/build.xml # and nbproject/project.properties in the same dir. Needs to be looked at. diff -Nru openjdk-17-17.0.2+8/make/autoconf/basic.m4 openjdk-17-17.0.3+7/make/autoconf/basic.m4 --- openjdk-17-17.0.2+8/make/autoconf/basic.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/basic.m4 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -94,11 +94,6 @@ # Locate the directory of this script. AUTOCONF_DIR=$TOPDIR/make/autoconf - - # Setup username (for use in adhoc version strings etc) - # Outer [ ] to quote m4. - [ USERNAME=`$ECHO "$USER" | $TR -d -c '[a-z][A-Z][0-9]'` ] - AC_SUBST(USERNAME) ]) ############################################################################### diff -Nru openjdk-17-17.0.2+8/make/autoconf/jdk-options.m4 openjdk-17-17.0.3+7/make/autoconf/jdk-options.m4 --- openjdk-17-17.0.2+8/make/autoconf/jdk-options.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/jdk-options.m4 2022-04-19 19:55:43.000000000 +0000 @@ -169,6 +169,23 @@ fi AC_SUBST(CACERTS_FILE) + # Choose cacerts source folder for user provided PEM files + AC_ARG_WITH(cacerts-src, [AS_HELP_STRING([--with-cacerts-src], + [specify alternative cacerts source folder containing certificates])]) + CACERTS_SRC="" + AC_MSG_CHECKING([for cacerts source]) + if test "x$with_cacerts_src" == x; then + AC_MSG_RESULT([default]) + else + CACERTS_SRC=$with_cacerts_src + if test ! -d "$CACERTS_SRC"; then + AC_MSG_RESULT([fail]) + AC_MSG_ERROR([Specified cacerts source folder "$CACERTS_SRC" does not exist]) + fi + AC_MSG_RESULT([$CACERTS_SRC]) + fi + AC_SUBST(CACERTS_SRC) + # Enable or disable unlimited crypto UTIL_ARG_ENABLE(NAME: unlimited-crypto, DEFAULT: true, RESULT: UNLIMITED_CRYPTO, DESC: [enable unlimited crypto policy]) diff -Nru openjdk-17-17.0.2+8/make/autoconf/jdk-version.m4 openjdk-17-17.0.3+7/make/autoconf/jdk-version.m4 --- openjdk-17-17.0.2+8/make/autoconf/jdk-version.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/jdk-version.m4 2022-04-19 19:55:43.000000000 +0000 @@ -69,6 +69,17 @@ AC_SUBST(JDK_RC_PLATFORM_NAME) AC_SUBST(HOTSPOT_VM_DISTRO) + # Setup username (for use in adhoc version strings etc) + AC_ARG_WITH([build-user], [AS_HELP_STRING([--with-build-user], + [build username to use in version strings])]) + if test "x$with_build_user" != x; then + USERNAME="$with_build_user" + else + # Outer [ ] to quote m4. + [ USERNAME=`$ECHO "$USER" | $TR -d -c '[a-z][A-Z][0-9]'` ] + fi + AC_SUBST(USERNAME) + # Set the JDK RC name AC_ARG_WITH(jdk-rc-name, [AS_HELP_STRING([--with-jdk-rc-name], [Set JDK RC name. This is used for FileDescription and ProductName properties diff -Nru openjdk-17-17.0.2+8/make/autoconf/spec.gmk.in openjdk-17-17.0.3+7/make/autoconf/spec.gmk.in --- openjdk-17-17.0.2+8/make/autoconf/spec.gmk.in 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/spec.gmk.in 2022-04-19 19:55:43.000000000 +0000 @@ -409,6 +409,8 @@ # Source file for cacerts CACERTS_FILE=@CACERTS_FILE@ +# Source folder for user provided cacerts PEM files +CACERTS_SRC=@CACERTS_SRC@ # Enable unlimited crypto policy UNLIMITED_CRYPTO=@UNLIMITED_CRYPTO@ diff -Nru openjdk-17-17.0.2+8/make/autoconf/toolchain.m4 openjdk-17-17.0.3+7/make/autoconf/toolchain.m4 --- openjdk-17-17.0.2+8/make/autoconf/toolchain.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/toolchain.m4 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -234,7 +234,7 @@ if test "x$OPENJDK_TARGET_OS" = xmacosx; then if test -n "$XCODEBUILD"; then # On Mac OS X, default toolchain to clang after Xcode 5 - XCODE_VERSION_OUTPUT=`"$XCODEBUILD" -version 2>&1 | $HEAD -n 1` + XCODE_VERSION_OUTPUT=`"$XCODEBUILD" -version | $HEAD -n 1` $ECHO "$XCODE_VERSION_OUTPUT" | $GREP "Xcode " > /dev/null if test $? -ne 0; then AC_MSG_NOTICE([xcodebuild output: $XCODE_VERSION_OUTPUT]) diff -Nru openjdk-17-17.0.2+8/make/autoconf/toolchain_microsoft.m4 openjdk-17-17.0.3+7/make/autoconf/toolchain_microsoft.m4 --- openjdk-17-17.0.2+8/make/autoconf/toolchain_microsoft.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/toolchain_microsoft.m4 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ ################################################################################ # The order of these defines the priority by which we try to find them. -VALID_VS_VERSIONS="2019 2017" +VALID_VS_VERSIONS="2019 2017 2022" VS_DESCRIPTION_2017="Microsoft Visual Studio 2017" VS_VERSION_INTERNAL_2017=141 @@ -56,6 +56,21 @@ VS_SUPPORTED_2019=true VS_TOOLSET_SUPPORTED_2019=true +VS_DESCRIPTION_2022="Microsoft Visual Studio 2022" +VS_VERSION_INTERNAL_2022=143 +VS_MSVCR_2022=vcruntime140.dll +VS_VCRUNTIME_1_2022=vcruntime140_1.dll +VS_MSVCP_2022=msvcp140.dll +VS_ENVVAR_2022="VS170COMNTOOLS" +VS_USE_UCRT_2022="true" +VS_VS_INSTALLDIR_2022="Microsoft Visual Studio/2022" +VS_EDITIONS_2022="BuildTools Community Professional Enterprise" +VS_SDK_INSTALLDIR_2022= +VS_VS_PLATFORM_NAME_2022="v143" +VS_SDK_PLATFORM_NAME_2022= +VS_SUPPORTED_2022=true +VS_TOOLSET_SUPPORTED_2022=true + ################################################################################ AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT], diff -Nru openjdk-17-17.0.2+8/make/common/ZipArchive.gmk openjdk-17-17.0.3+7/make/common/ZipArchive.gmk --- openjdk-17-17.0.2+8/make/common/ZipArchive.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/common/ZipArchive.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,9 @@ ifndef _ZIP_ARCHIVE_GMK _ZIP_ARCHIVE_GMK := 1 +# Depends on build tools for MakeZipReproducible +include ../ToolsJdk.gmk + ifeq (,$(_MAKEBASE_GMK)) $(error You must include MakeBase.gmk prior to including ZipArchive.gmk) endif @@ -51,6 +54,8 @@ # FOLLOW_SYMLINKS - Set to explicitly follow symlinks. Affects performance of # finding files. # ZIP_OPTIONS extra options to pass to zip +# REPRODUCIBLE override ENABLE_REPRODUCIBLE_BUILD (to make zip reproducible or not) + SetupZipArchive = $(NamedParamsMacroTemplate) define SetupZipArchiveBody @@ -124,6 +129,10 @@ ) \ ) + ifeq ($$($1_REPRODUCIBLE), ) + $1_REPRODUCIBLE := $$(ENABLE_REPRODUCIBLE_BUILD) + endif + # Use a slightly shorter name for logging, but with enough path to identify this zip. $1_NAME:=$$(subst $$(OUTPUTDIR)/,,$$($1_ZIP)) @@ -134,6 +143,8 @@ # dir is very small. # If zip has nothing to do, it returns 12 and would fail the build. Check for 12 # and only fail if it's not. + # For reproducible builds set the zip access & modify times to SOURCE_DATE_EPOCH + # by using a ziptmp folder to generate final zip from using MakeZipReproducible. $$($1_ZIP) : $$($1_ALL_SRCS) $$($1_EXTRA_DEPS) $$(call LogWarn, Updating $$($1_NAME)) $$(call MakeTargetDir) @@ -163,7 +174,18 @@ $$($1_ZIP_EXCLUDES_$$s) \ || test "$$$$?" = "12" \ ))$$(NEWLINE) \ - ) true \ + ) true + ifeq ($$($1_REPRODUCIBLE), true) + $$(call ExecuteWithLog, \ + $$(SUPPORT_OUTPUTDIR)/makezipreproducible/$$(patsubst $$(OUTPUTDIR)/%,%, $$@), \ + ($(RM) $$(SUPPORT_OUTPUTDIR)/ziptmp/$1/tmp.zip && \ + $(MKDIR) -p $$(SUPPORT_OUTPUTDIR)/ziptmp/$1 && \ + $(TOOL_MAKEZIPREPRODUCIBLE) -f $$(SUPPORT_OUTPUTDIR)/ziptmp/$1/tmp.zip \ + -t $(SOURCE_DATE_EPOCH) $$@ && \ + $(RM) $$@ && \ + $(MV) $$(SUPPORT_OUTPUTDIR)/ziptmp/$1/tmp.zip $$@ \ + )) + endif $(TOUCH) $$@ # Add zip to target list diff -Nru openjdk-17-17.0.2+8/make/conf/version-numbers.conf openjdk-17-17.0.3+7/make/conf/version-numbers.conf --- openjdk-17-17.0.2+8/make/conf/version-numbers.conf 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/conf/version-numbers.conf 2022-04-19 19:55:43.000000000 +0000 @@ -28,12 +28,12 @@ DEFAULT_VERSION_FEATURE=17 DEFAULT_VERSION_INTERIM=0 -DEFAULT_VERSION_UPDATE=2 +DEFAULT_VERSION_UPDATE=3 DEFAULT_VERSION_PATCH=0 DEFAULT_VERSION_EXTRA1=0 DEFAULT_VERSION_EXTRA2=0 DEFAULT_VERSION_EXTRA3=0 -DEFAULT_VERSION_DATE=2022-01-18 +DEFAULT_VERSION_DATE=2022-04-19 DEFAULT_VERSION_CLASSFILE_MAJOR=61 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 diff -Nru openjdk-17-17.0.2+8/make/data/currency/CurrencyData.properties openjdk-17-17.0.3+7/make/data/currency/CurrencyData.properties --- openjdk-17-17.0.2+8/make/data/currency/CurrencyData.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/data/currency/CurrencyData.properties 2022-04-19 19:55:43.000000000 +0000 @@ -32,7 +32,7 @@ # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=169 +dataVersion=170 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. @@ -54,7 +54,7 @@ SBD090-SCR690-SDD736-SDG938-SEK752-SGD702-SHP654-SIT705-SKK703-SLL694-SOS706-\ SRD968-SRG740-SSP728-STD678-STN930-SVC222-SYP760-SZL748-THB764-TJS972-TMM795-TMT934-TND788-TOP776-\ TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-UYI940-\ - UYU858-UZS860-VEB862-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ + UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ XBB956-XBC957-XBD958-XCD951-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\ XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWL932-\ ZWN942-ZWR935 diff -Nru openjdk-17-17.0.2+8/make/devkit/createJMHBundle.sh openjdk-17-17.0.3+7/make/devkit/createJMHBundle.sh --- openjdk-17-17.0.2+8/make/devkit/createJMHBundle.sh 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/devkit/createJMHBundle.sh 2022-04-19 19:55:43.000000000 +0000 @@ -26,7 +26,7 @@ # Create a bundle in the build directory, containing what's needed to # build and run JMH microbenchmarks from the OpenJDK build. -JMH_VERSION=1.32 +JMH_VERSION=1.34 COMMONS_MATH3_VERSION=3.2 JOPT_SIMPLE_VERSION=4.6 diff -Nru openjdk-17-17.0.2+8/make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java openjdk-17-17.0.3+7/make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java --- openjdk-17-17.0.2+8/make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package build.tools.makezipreproducible; + +import java.io.*; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +/** + * Generate a zip file in a "reproducible" manner from the input zip file. + * Standard zip tools rely on OS file list querying whose ordering can vary + * by platform architecture, this class ensures the zip entries are ordered + * and also supports SOURCE_DATE_EPOCH timestamps. + */ +public class MakeZipReproducible { + String input_file = null; + String fname = null; + String zname = ""; + long timestamp = -1L; + boolean verbose = false; + + // Keep a sorted Set of ZipEntrys to be processed, so that the zip is reproducible + SortedMap entries = new TreeMap(); + + private boolean ok; + + public MakeZipReproducible() { + } + + public synchronized boolean run(String args[]) { + ok = true; + if (!parseArgs(args)) { + return false; + } + try { + zname = fname.replace(File.separatorChar, '/'); + if (zname.startsWith("./")) { + zname = zname.substring(2); + } + + if (verbose) System.out.println("Input zip file: " + input_file); + + File inFile = new File(input_file); + if (!inFile.exists()) { + error("Input zip file does not exist"); + ok = false; + } else { + File zipFile = new File(fname); + // Check archive to create does not exist + if (!zipFile.exists()) { + // Process input ZipEntries + ok = processInputEntries(inFile); + if (ok) { + try (FileOutputStream out = new FileOutputStream(fname)) { + ok = create(inFile, new BufferedOutputStream(out, 4096)); + } + } else { + } + } else { + error("Target zip file "+fname+" already exists."); + ok = false; + } + } + } catch (IOException e) { + fatalError(e); + ok = false; + } catch (Error ee) { + ee.printStackTrace(); + ok = false; + } catch (Throwable t) { + t.printStackTrace(); + ok = false; + } + return ok; + } + + boolean parseArgs(String args[]) { + try { + boolean parsingIncludes = false; + boolean parsingExcludes = false; + int count = 0; + while(count < args.length) { + if (args[count].startsWith("-")) { + String flag = args[count].substring(1); + switch (flag.charAt(0)) { + case 'f': + fname = args[++count]; + break; + case 't': + // SOURCE_DATE_EPOCH timestamp specified + timestamp = Long.parseLong(args[++count]) * 1000; + break; + case 'v': + verbose = true; + break; + default: + error(String.format("Illegal option -%s", String.valueOf(flag.charAt(0)))); + usageError(); + return false; + } + } else { + // input zip file + if (input_file != null) { + error("Input zip file already specified"); + usageError(); + return false; + } + input_file = args[count]; + } + count++; + } + } catch (ArrayIndexOutOfBoundsException e) { + usageError(); + return false; + } catch (NumberFormatException e) { + usageError(); + return false; + } + if (fname == null) { + error("-f must be specified"); + usageError(); + return false; + } + // If no files specified then default to current directory + if (input_file == null) { + error("No input zip file specified"); + usageError(); + return false; + } + + return true; + } + + // Process input zip file and add to sorted entries set + boolean processInputEntries(File inFile) throws IOException { + ZipFile zipFile = new ZipFile(inFile); + zipFile.stream().forEach(entry -> entries.put(entry.getName(), entry)); + + return true; + } + + // Create new zip from entries + boolean create(File inFile, OutputStream out) throws IOException + { + try (ZipFile zipFile = new ZipFile(inFile); + ZipOutputStream zos = new ZipOutputStream(out)) { + for (Map.Entry entry : entries.entrySet()) { + ZipEntry zipEntry = entry.getValue(); + if (zipEntry.getSize() > 0) { + try (InputStream eis = zipFile.getInputStream(zipEntry)) { + addEntry(zos, zipEntry, eis); + } + } else { + addEntry(zos, zipEntry, null); + } + } + } + return true; + } + + // Add Entry and data to Zip + void addEntry(ZipOutputStream zos, ZipEntry entry, InputStream entryInputStream) throws IOException { + if (verbose) { + System.out.println("Adding: "+entry.getName()); + } + + // Set to specified timestamp if set otherwise leave as original lastModified time + if (timestamp != -1L) { + entry.setTime(timestamp); + } + + zos.putNextEntry(entry); + if (entry.getSize() > 0 && entryInputStream != null) { + entryInputStream.transferTo(zos); + } + zos.closeEntry(); + } + + void usageError() { + error( + "Usage: MakeZipReproducible [-v] [-t ] -f \n" + + "Options:\n" + + " -v verbose output\n" + + " -f specify archive file name to create\n" + + " -t specific SOURCE_DATE_EPOCH value to use for timestamps\n" + + " input_zip_file re-written as a reproducible zip output_zip_file.\n"); + } + + void fatalError(Exception e) { + e.printStackTrace(); + } + + protected void error(String s) { + System.err.println(s); + } + + public static void main(String args[]) { + MakeZipReproducible z = new MakeZipReproducible(); + System.exit(z.run(args) ? 0 : 1); + } +} + diff -Nru openjdk-17-17.0.2+8/make/modules/java.base/Gendata.gmk openjdk-17-17.0.3+7/make/modules/java.base/Gendata.gmk --- openjdk-17-17.0.2+8/make/modules/java.base/Gendata.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/modules/java.base/Gendata.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -60,7 +60,11 @@ ################################################################################ -GENDATA_CACERTS_SRC := $(TOPDIR)/make/data/cacerts/ +ifneq ($(CACERTS_SRC), ) + GENDATA_CACERTS_SRC := $(CACERTS_SRC) +else + GENDATA_CACERTS_SRC := $(TOPDIR)/make/data/cacerts/ +endif GENDATA_CACERTS := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/security/cacerts $(GENDATA_CACERTS): $(BUILD_TOOLS_JDK) $(wildcard $(GENDATA_CACERTS_SRC)/*) diff -Nru openjdk-17-17.0.2+8/make/modules/jdk.javadoc/Gendata.gmk openjdk-17-17.0.3+7/make/modules/jdk.javadoc/Gendata.gmk --- openjdk-17-17.0.2+8/make/modules/jdk.javadoc/Gendata.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/modules/jdk.javadoc/Gendata.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -72,7 +72,9 @@ $(MODULE_INFOS) $(call MakeTargetDir) $(call LogInfo, Creating javadoc element lists) - $(RM) -r $(ELEMENT_LISTS_DIR) + $(RM) $(ELEMENT_LISTS_DIR)/element-list-{$(call CommaList, \ + $(call sequence, $(GENERATE_SYMBOLS_FROM_JDK_VERSION), \ + $(JDK_SOURCE_TARGET_VERSION)))}.txt # Generate element-list files for JDK 11 to current-1 $(call ExecuteWithLog, $@_historic, \ $(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \ diff -Nru openjdk-17-17.0.2+8/make/test/JtregNativeHotspot.gmk openjdk-17-17.0.3+7/make/test/JtregNativeHotspot.gmk --- openjdk-17-17.0.2+8/make/test/JtregNativeHotspot.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/test/JtregNativeHotspot.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -863,7 +863,7 @@ BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeFPRegs := -ldl BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libAsyncGetCallTraceTest := -ldl else - BUILD_HOTSPOT_JTREG_EXCLUDE += libtest-rw.c libtest-rwx.c libTestJNI.c \ + BUILD_HOTSPOT_JTREG_EXCLUDE += libtest-rw.c libtest-rwx.c \ exeinvoke.c exestack-gap.c exestack-tls.c libAsyncGetCallTraceTest.cpp endif @@ -871,7 +871,7 @@ ifeq ($(call isTargetOs, windows), true) BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT - BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c + BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libbootclssearch_agent += -lpthread diff -Nru openjdk-17-17.0.2+8/src/demo/share/jfc/SwingSet2/TableDemo.java openjdk-17-17.0.3+7/src/demo/share/jfc/SwingSet2/TableDemo.java --- openjdk-17-17.0.2+8/src/demo/share/jfc/SwingSet2/TableDemo.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/demo/share/jfc/SwingSet2/TableDemo.java 2022-04-19 19:55:43.000000000 +0000 @@ -741,4 +741,13 @@ footerTextField.setDragEnabled(dragEnabled); } + @Override + public ImageIcon createImageIcon(String filename, String description) { + ImageIcon imageIcon = super.createImageIcon(filename, description); + AccessibleContext context = imageIcon.getAccessibleContext(); + if (context!= null) { + context.setAccessibleName(description); + } + return imageIcon; + } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/aarch64.ad openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/aarch64.ad --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/aarch64.ad 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/aarch64.ad 2022-04-19 19:55:43.000000000 +0000 @@ -2373,6 +2373,8 @@ bool ret_value = true; switch (opcode) { + case Op_OnSpinWait: + return VM_Version::supports_on_spin_wait(); case Op_CacheWB: case Op_CacheWBPreSync: case Op_CacheWBPostSync: @@ -3848,7 +3850,7 @@ // Try to CAS m->owner from NULL to current thread. __ add(tmp, disp_hdr, (ObjectMonitor::owner_offset_in_bytes()-markWord::monitor_value)); __ cmpxchg(tmp, zr, rthread, Assembler::xword, /*acquire*/ true, - /*release*/ true, /*weak*/ false, noreg); // Sets flags for result + /*release*/ true, /*weak*/ false, rscratch1); // Sets flags for result // Store a non-null value into the box to avoid looking like a re-entrant // lock. The fast-path monitor unlock code checks for @@ -3857,6 +3859,15 @@ __ mov(tmp, (address)markWord::unused_mark().value()); __ str(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); + __ br(Assembler::EQ, cont); // CAS success means locking succeeded + + __ cmp(rscratch1, rthread); + __ br(Assembler::NE, cont); // Check for recursive locking + + // Recursive lock case + __ increment(Address(disp_hdr, ObjectMonitor::recursions_offset_in_bytes() - markWord::monitor_value), 1); + // flag == EQ still from the cmp above, checking if this is a reentrant lock + __ bind(cont); // flag == EQ indicates success // flag == NE indicates failure @@ -3904,11 +3915,20 @@ __ add(tmp, tmp, -(int)markWord::monitor_value); // monitor __ ldr(rscratch1, Address(tmp, ObjectMonitor::owner_offset_in_bytes())); __ ldr(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset_in_bytes())); - __ eor(rscratch1, rscratch1, rthread); // Will be 0 if we are the owner. - __ orr(rscratch1, rscratch1, disp_hdr); // Will be 0 if there are 0 recursions - __ cmp(rscratch1, zr); // Sets flags for result + + Label notRecursive; + __ cmp(rscratch1, rthread); __ br(Assembler::NE, cont); + __ cbz(disp_hdr, notRecursive); + + // Recursive lock + __ sub(disp_hdr, disp_hdr, 1u); + __ str(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset_in_bytes())); + // flag == EQ was set in the ownership check above + __ b(cont); + + __ bind(notRecursive); __ ldr(rscratch1, Address(tmp, ObjectMonitor::EntryList_offset_in_bytes())); __ ldr(disp_hdr, Address(tmp, ObjectMonitor::cxq_offset_in_bytes())); __ orr(rscratch1, rscratch1, disp_hdr); // Will be 0 if both are 0. @@ -14321,6 +14341,18 @@ ins_pipe(fp_uop_d); %} +instruct onspinwait() %{ + match(OnSpinWait); + ins_cost(INSN_COST); + + format %{ "onspinwait" %} + + ins_encode %{ + __ spin_wait(); + %} + ins_pipe(pipe_class_empty); +%} + // ============================================================================ // Logical Instructions diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2988,7 +2988,7 @@ void LIR_Assembler::membar_storeload() { __ membar(MacroAssembler::StoreLoad); } void LIR_Assembler::on_spin_wait() { - Unimplemented(); + __ spin_wait(); } void LIR_Assembler::get_thread(LIR_Opr result_reg) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -355,7 +355,7 @@ } -void C1_MacroAssembler::verified_entry() { +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { // If we have to make this method not-entrant we'll overwrite its // first instruction with a jump. For this action to be legal we // must ensure that this first instruction is a B, BL, NOP, BKPT, diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/globals_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/globals_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/globals_aarch64.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/globals_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -111,7 +111,15 @@ product(int, SoftwarePrefetchHintDistance, -1, \ "Use prfm hint with specified distance in compiled code." \ "Value -1 means off.") \ - range(-1, 4096) + range(-1, 4096) \ + product(ccstr, OnSpinWaitInst, "none", DIAGNOSTIC, \ + "The instruction to use to implement " \ + "java.lang.Thread.onSpinWait()." \ + "Options: none, nop, isb, yield.") \ + product(uint, OnSpinWaitInstCount, 1, DIAGNOSTIC, \ + "The number of OnSpinWaitInst instructions to generate." \ + "It cannot be used with OnSpinWaitInst=none.") \ + range(1, 99) // end of ARCH_FLAGS diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2027,15 +2027,6 @@ str(rscratch1, dst); } - -void MacroAssembler::pusha() { - push(0x7fffffff, sp); -} - -void MacroAssembler::popa() { - pop(0x7fffffff, sp); -} - // Push lots of registers in the bit set supplied. Don't push sp. // Return the number of words pushed int MacroAssembler::push(unsigned int bitset, Register stack) { @@ -2677,7 +2668,7 @@ void MacroAssembler::push_CPU_state(bool save_vectors, bool use_sve, int sve_vector_size_in_bytes) { - push(0x3fffffff, sp); // integer registers except lr & sp + push(RegSet::range(r0, r29), sp); // integer registers except lr & sp if (save_vectors && use_sve && sve_vector_size_in_bytes > 16) { sub(sp, sp, sve_vector_size_in_bytes * FloatRegisterImpl::number_of_registers); for (int i = 0; i < FloatRegisterImpl::number_of_registers; i++) { @@ -2713,7 +2704,14 @@ reinitialize_ptrue(); } - pop(0x3fffffff, sp); // integer registers except lr & sp + // integer registers except lr & sp + pop(RegSet::range(r0, r17), sp); +#ifdef R18_RESERVED + ldp(zr, r19, Address(post(sp, 2 * wordSize))); + pop(RegSet::range(r20, r29), sp); +#else + pop(RegSet::range(r18_tls, r29), sp); +#endif } /** @@ -5354,3 +5352,21 @@ } } #endif + +void MacroAssembler::spin_wait() { + for (int i = 0; i < VM_Version::spin_wait_desc().inst_count(); ++i) { + switch (VM_Version::spin_wait_desc().inst()) { + case SpinWait::NOP: + nop(); + break; + case SpinWait::ISB: + isb(); + break; + case SpinWait::YIELD: + yield(); + break; + default: + ShouldNotReachHere(); + } + } +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1123,10 +1123,6 @@ void push(Register src); void pop(Register dst); - // push all registers onto the stack - void pusha(); - void popa(); - void repne_scan(Register addr, Register value, Register count, Register scratch); void repne_scanw(Register addr, Register value, Register count, @@ -1414,6 +1410,9 @@ void cache_wb(Address line); void cache_wbsync(bool is_pre); + // Code for java.lang.Thread::onSpinWait() intrinsic. + void spin_wait(); + private: // Check the current thread doesn't need a cross modify fence. void verify_cross_modify_fence_not_required() PRODUCT_RETURN; diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Amazon.com Inc. or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef CPU_AARCH64_SPIN_WAIT_AARCH64_HPP +#define CPU_AARCH64_SPIN_WAIT_AARCH64_HPP + +class SpinWait { +public: + enum Inst { + NONE = -1, + NOP, + ISB, + YIELD + }; + +private: + Inst _inst; + int _count; + +public: + SpinWait(Inst inst = NONE, int count = 0) : _inst(inst), _count(count) {} + + Inst inst() const { return _inst; } + int inst_count() const { return _count; } +}; + +#endif // CPU_AARCH64_SPIN_WAIT_AARCH64_HPP diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -3231,6 +3231,194 @@ // c_rarg2 - int offset // c_rarg3 - int limit // + address generate_md5_implCompress(bool multi_block, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Register buf = c_rarg0; + Register state = c_rarg1; + Register ofs = c_rarg2; + Register limit = c_rarg3; + Register a = r4; + Register b = r5; + Register c = r6; + Register d = r7; + Register rscratch3 = r10; + Register rscratch4 = r11; + + Label keys; + Label md5_loop; + + __ BIND(md5_loop); + + // Save hash values for addition after rounds + __ ldrw(a, Address(state, 0)); + __ ldrw(b, Address(state, 4)); + __ ldrw(c, Address(state, 8)); + __ ldrw(d, Address(state, 12)); + +#define FF(r1, r2, r3, r4, k, s, t) \ + __ eorw(rscratch3, r3, r4); \ + __ movw(rscratch2, t); \ + __ andw(rscratch3, rscratch3, r2); \ + __ addw(rscratch4, r1, rscratch2); \ + __ ldrw(rscratch1, Address(buf, k*4)); \ + __ eorw(rscratch3, rscratch3, r4); \ + __ addw(rscratch3, rscratch3, rscratch1); \ + __ addw(rscratch3, rscratch3, rscratch4); \ + __ rorw(rscratch2, rscratch3, 32 - s); \ + __ addw(r1, rscratch2, r2); + +#define GG(r1, r2, r3, r4, k, s, t) \ + __ eorw(rscratch2, r2, r3); \ + __ ldrw(rscratch1, Address(buf, k*4)); \ + __ andw(rscratch3, rscratch2, r4); \ + __ movw(rscratch2, t); \ + __ eorw(rscratch3, rscratch3, r3); \ + __ addw(rscratch4, r1, rscratch2); \ + __ addw(rscratch3, rscratch3, rscratch1); \ + __ addw(rscratch3, rscratch3, rscratch4); \ + __ rorw(rscratch2, rscratch3, 32 - s); \ + __ addw(r1, rscratch2, r2); + +#define HH(r1, r2, r3, r4, k, s, t) \ + __ eorw(rscratch3, r3, r4); \ + __ movw(rscratch2, t); \ + __ addw(rscratch4, r1, rscratch2); \ + __ ldrw(rscratch1, Address(buf, k*4)); \ + __ eorw(rscratch3, rscratch3, r2); \ + __ addw(rscratch3, rscratch3, rscratch1); \ + __ addw(rscratch3, rscratch3, rscratch4); \ + __ rorw(rscratch2, rscratch3, 32 - s); \ + __ addw(r1, rscratch2, r2); + +#define II(r1, r2, r3, r4, k, s, t) \ + __ movw(rscratch3, t); \ + __ ornw(rscratch2, r2, r4); \ + __ addw(rscratch4, r1, rscratch3); \ + __ ldrw(rscratch1, Address(buf, k*4)); \ + __ eorw(rscratch3, rscratch2, r3); \ + __ addw(rscratch3, rscratch3, rscratch1); \ + __ addw(rscratch3, rscratch3, rscratch4); \ + __ rorw(rscratch2, rscratch3, 32 - s); \ + __ addw(r1, rscratch2, r2); + + // Round 1 + FF(a, b, c, d, 0, 7, 0xd76aa478) + FF(d, a, b, c, 1, 12, 0xe8c7b756) + FF(c, d, a, b, 2, 17, 0x242070db) + FF(b, c, d, a, 3, 22, 0xc1bdceee) + FF(a, b, c, d, 4, 7, 0xf57c0faf) + FF(d, a, b, c, 5, 12, 0x4787c62a) + FF(c, d, a, b, 6, 17, 0xa8304613) + FF(b, c, d, a, 7, 22, 0xfd469501) + FF(a, b, c, d, 8, 7, 0x698098d8) + FF(d, a, b, c, 9, 12, 0x8b44f7af) + FF(c, d, a, b, 10, 17, 0xffff5bb1) + FF(b, c, d, a, 11, 22, 0x895cd7be) + FF(a, b, c, d, 12, 7, 0x6b901122) + FF(d, a, b, c, 13, 12, 0xfd987193) + FF(c, d, a, b, 14, 17, 0xa679438e) + FF(b, c, d, a, 15, 22, 0x49b40821) + + // Round 2 + GG(a, b, c, d, 1, 5, 0xf61e2562) + GG(d, a, b, c, 6, 9, 0xc040b340) + GG(c, d, a, b, 11, 14, 0x265e5a51) + GG(b, c, d, a, 0, 20, 0xe9b6c7aa) + GG(a, b, c, d, 5, 5, 0xd62f105d) + GG(d, a, b, c, 10, 9, 0x02441453) + GG(c, d, a, b, 15, 14, 0xd8a1e681) + GG(b, c, d, a, 4, 20, 0xe7d3fbc8) + GG(a, b, c, d, 9, 5, 0x21e1cde6) + GG(d, a, b, c, 14, 9, 0xc33707d6) + GG(c, d, a, b, 3, 14, 0xf4d50d87) + GG(b, c, d, a, 8, 20, 0x455a14ed) + GG(a, b, c, d, 13, 5, 0xa9e3e905) + GG(d, a, b, c, 2, 9, 0xfcefa3f8) + GG(c, d, a, b, 7, 14, 0x676f02d9) + GG(b, c, d, a, 12, 20, 0x8d2a4c8a) + + // Round 3 + HH(a, b, c, d, 5, 4, 0xfffa3942) + HH(d, a, b, c, 8, 11, 0x8771f681) + HH(c, d, a, b, 11, 16, 0x6d9d6122) + HH(b, c, d, a, 14, 23, 0xfde5380c) + HH(a, b, c, d, 1, 4, 0xa4beea44) + HH(d, a, b, c, 4, 11, 0x4bdecfa9) + HH(c, d, a, b, 7, 16, 0xf6bb4b60) + HH(b, c, d, a, 10, 23, 0xbebfbc70) + HH(a, b, c, d, 13, 4, 0x289b7ec6) + HH(d, a, b, c, 0, 11, 0xeaa127fa) + HH(c, d, a, b, 3, 16, 0xd4ef3085) + HH(b, c, d, a, 6, 23, 0x04881d05) + HH(a, b, c, d, 9, 4, 0xd9d4d039) + HH(d, a, b, c, 12, 11, 0xe6db99e5) + HH(c, d, a, b, 15, 16, 0x1fa27cf8) + HH(b, c, d, a, 2, 23, 0xc4ac5665) + + // Round 4 + II(a, b, c, d, 0, 6, 0xf4292244) + II(d, a, b, c, 7, 10, 0x432aff97) + II(c, d, a, b, 14, 15, 0xab9423a7) + II(b, c, d, a, 5, 21, 0xfc93a039) + II(a, b, c, d, 12, 6, 0x655b59c3) + II(d, a, b, c, 3, 10, 0x8f0ccc92) + II(c, d, a, b, 10, 15, 0xffeff47d) + II(b, c, d, a, 1, 21, 0x85845dd1) + II(a, b, c, d, 8, 6, 0x6fa87e4f) + II(d, a, b, c, 15, 10, 0xfe2ce6e0) + II(c, d, a, b, 6, 15, 0xa3014314) + II(b, c, d, a, 13, 21, 0x4e0811a1) + II(a, b, c, d, 4, 6, 0xf7537e82) + II(d, a, b, c, 11, 10, 0xbd3af235) + II(c, d, a, b, 2, 15, 0x2ad7d2bb) + II(b, c, d, a, 9, 21, 0xeb86d391) + +#undef FF +#undef GG +#undef HH +#undef II + + // write hash values back in the correct order + __ ldrw(rscratch1, Address(state, 0)); + __ addw(rscratch1, rscratch1, a); + __ strw(rscratch1, Address(state, 0)); + + __ ldrw(rscratch2, Address(state, 4)); + __ addw(rscratch2, rscratch2, b); + __ strw(rscratch2, Address(state, 4)); + + __ ldrw(rscratch3, Address(state, 8)); + __ addw(rscratch3, rscratch3, c); + __ strw(rscratch3, Address(state, 8)); + + __ ldrw(rscratch4, Address(state, 12)); + __ addw(rscratch4, rscratch4, d); + __ strw(rscratch4, Address(state, 12)); + + if (multi_block) { + __ add(buf, buf, 64); + __ add(ofs, ofs, 64); + __ cmp(ofs, limit); + __ br(Assembler::LE, md5_loop); + __ mov(c_rarg0, ofs); // return ofs + } + + __ ret(lr); + + return start; + } + + // Arguments: + // + // Inputs: + // c_rarg0 - byte[] source+offset + // c_rarg1 - int[] SHA.state + // c_rarg2 - int offset + // c_rarg3 - int limit + // address generate_sha1_implCompress(bool multi_block, const char *name) { __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); @@ -6182,6 +6370,18 @@ return start; } + // Support for spin waits. + address generate_spin_wait() { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "spin_wait"); + address start = __ pc(); + + __ spin_wait(); + __ ret(lr); + + return start; + } + #ifdef LINUX // ARMv8.1 LSE versions of the atomic stubs used by Atomic::PlatformXX. @@ -7464,6 +7664,10 @@ StubRoutines::_counterMode_AESCrypt = generate_counterMode_AESCrypt(); } + if (UseMD5Intrinsics) { + StubRoutines::_md5_implCompress = generate_md5_implCompress(false, "md5_implCompress"); + StubRoutines::_md5_implCompressMB = generate_md5_implCompress(true, "md5_implCompressMB"); + } if (UseSHA1Intrinsics) { StubRoutines::_sha1_implCompress = generate_sha1_implCompress(false, "sha1_implCompress"); StubRoutines::_sha1_implCompressMB = generate_sha1_implCompress(true, "sha1_implCompressMB"); @@ -7486,6 +7690,8 @@ StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32(); } + StubRoutines::aarch64::_spin_wait = generate_spin_wait(); + #ifdef LINUX generate_atomic_entry_points(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -57,6 +57,10 @@ address StubRoutines::aarch64::_string_indexof_linear_ul = NULL; address StubRoutines::aarch64::_large_byte_array_inflate = NULL; address StubRoutines::aarch64::_method_entry_barrier = NULL; + +static void empty_spin_wait() { } +address StubRoutines::aarch64::_spin_wait = CAST_FROM_FN_PTR(address, empty_spin_wait); + bool StubRoutines::aarch64::_completed = false; /** diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -36,7 +36,7 @@ enum platform_dependent_constants { code_size1 = 19000, // simply increase if too small (assembler will crash if too small) - code_size2 = 32000 // simply increase if too small (assembler will crash if too small) + code_size2 = 45000 // simply increase if too small (assembler will crash if too small) }; class aarch64 { @@ -72,6 +72,8 @@ static address _method_entry_barrier; + static address _spin_wait; + static bool _completed; public: @@ -177,6 +179,10 @@ return _method_entry_barrier; } + static address spin_wait() { + return _spin_wait; + } + static bool complete() { return _completed; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1397,11 +1397,12 @@ __ cmp(rscratch1, (u1)StackOverflow::stack_guard_yellow_reserved_disabled); __ br(Assembler::NE, no_reguard); - __ pusha(); // XXX only save smashed registers + __ push_call_clobbered_registers(); __ mov(c_rarg0, rthread); __ mov(rscratch2, CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); __ blr(rscratch2); - __ popa(); // XXX only restore smashed registers + __ pop_call_clobbered_registers(); + __ bind(no_reguard); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -46,6 +46,26 @@ int VM_Version::_icache_line_size; int VM_Version::_initial_sve_vector_length; +SpinWait VM_Version::_spin_wait; + +static SpinWait get_spin_wait_desc() { + if (strcmp(OnSpinWaitInst, "nop") == 0) { + return SpinWait(SpinWait::NOP, OnSpinWaitInstCount); + } else if (strcmp(OnSpinWaitInst, "isb") == 0) { + return SpinWait(SpinWait::ISB, OnSpinWaitInstCount); + } else if (strcmp(OnSpinWaitInst, "yield") == 0) { + return SpinWait(SpinWait::YIELD, OnSpinWaitInstCount); + } else if (strcmp(OnSpinWaitInst, "none") != 0) { + vm_exit_during_initialization("The options for OnSpinWaitInst are nop, isb, yield, and none", OnSpinWaitInst); + } + + if (!FLAG_IS_DEFAULT(OnSpinWaitInstCount) && OnSpinWaitInstCount > 0) { + vm_exit_during_initialization("OnSpinWaitInstCount cannot be used for OnSpinWaitInst 'none'"); + } + + return SpinWait{}; +} + void VM_Version::initialize() { _supports_cx8 = true; _supports_atomic_getset4 = true; @@ -182,6 +202,14 @@ if (FLAG_IS_DEFAULT(UseSIMDForMemoryOps)) { FLAG_SET_DEFAULT(UseSIMDForMemoryOps, true); } + + if (FLAG_IS_DEFAULT(OnSpinWaitInst)) { + FLAG_SET_DEFAULT(OnSpinWaitInst, "isb"); + } + + if (FLAG_IS_DEFAULT(OnSpinWaitInstCount)) { + FLAG_SET_DEFAULT(OnSpinWaitInstCount, 1); + } } if (_cpu == CPU_ARM) { @@ -272,9 +300,8 @@ FLAG_SET_DEFAULT(UseFMA, true); } - if (UseMD5Intrinsics) { - warning("MD5 intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseMD5Intrinsics, false); + if (FLAG_IS_DEFAULT(UseMD5Intrinsics)) { + UseMD5Intrinsics = true; } if (_features & (CPU_SHA1 | CPU_SHA2 | CPU_SHA3 | CPU_SHA512)) { @@ -450,5 +477,7 @@ } #endif + _spin_wait = get_spin_wait_desc(); + UNSUPPORTED_OPTION(CriticalJNINatives); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -26,6 +26,7 @@ #ifndef CPU_AARCH64_VM_VERSION_AARCH64_HPP #define CPU_AARCH64_VM_VERSION_AARCH64_HPP +#include "spin_wait_aarch64.hpp" #include "runtime/abstract_vm_version.hpp" #include "utilities/sizes.hpp" @@ -45,6 +46,8 @@ static int _icache_line_size; static int _initial_sve_vector_length; + static SpinWait _spin_wait; + // Read additional info using OS-specific interfaces static void get_os_cpu_info(); @@ -142,6 +145,10 @@ static void get_compatible_board(char *buf, int buflen); + static const SpinWait& spin_wait_desc() { return _spin_wait; } + + static bool supports_on_spin_wait() { return _spin_wait.inst() != SpinWait::NONE; } + #ifdef __APPLE__ // Is the CPU running emulated (for example macOS Rosetta running x86_64 code on M1 ARM (aarch64) static bool is_cpu_emulated(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/arm/assembler_arm_32.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/arm/assembler_arm_32.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/arm/assembler_arm_32.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/arm/assembler_arm_32.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -47,7 +47,7 @@ // Convert the raw encoding form into the form expected by the // constructor for Address. Address Address::make_raw(int base, int index, int scale, int disp, relocInfo::relocType disp_reloc) { - RelocationHolder rspec; + RelocationHolder rspec = RelocationHolder::none; if (disp_reloc != relocInfo::none) { rspec = Relocation::spec_simple(disp_reloc); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1685,6 +1685,9 @@ } else { assert(right->is_constant(), "must be"); const uint c = (uint)right->as_constant_ptr()->as_jint(); + if (!Assembler::is_arith_imm_in_range(c)) { + BAILOUT("illegal arithmetic operand"); + } switch (code) { case lir_logic_and: __ and_32(res, lreg, c); break; case lir_logic_or: __ orr_32(res, lreg, c); break; @@ -1825,8 +1828,8 @@ __ teq(xhi, yhi); __ teq(xlo, ylo, eq); } else { - __ subs(xlo, xlo, ylo); - __ sbcs(xhi, xhi, yhi); + __ subs(Rtemp, xlo, ylo); + __ sbcs(Rtemp, xhi, yhi); } } else { ShouldNotReachHere(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -70,8 +70,8 @@ raw_pop(FP, LR); } -void C1_MacroAssembler::verified_entry() { - if (C1Breakpoint) { +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { + if (breakAtEntry) { breakpoint(); } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -81,8 +81,6 @@ if (_info->deoptimize_on_exception()) { address a = Runtime1::entry_for(Runtime1::predicate_failed_trap_id); - // May be used by optimizations like LoopInvariantCodeMotion or RangeCheckEliminator. - DEBUG_ONLY( __ untested("RangeCheckStub: predicate_failed_trap_id"); ) //__ load_const_optimized(R0, a); __ add_const_optimized(R0, R29_TOC, MacroAssembler::offset_to_global_toc(a)); __ mtctr(R0); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -87,8 +87,8 @@ } -void C1_MacroAssembler::verified_entry() { - if (C1Breakpoint) illtrap(); +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { + if (breakAtEntry) illtrap(); // build frame } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -41,17 +41,18 @@ // Compress char[] to byte[] by compressing 16 bytes at once. void C2_MacroAssembler::string_compress_16(Register src, Register dst, Register cnt, Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, - Label& Lfailure) { + Label& Lfailure, bool ascii) { const Register tmp0 = R0; + const int byte_mask = ascii ? 0x7F : 0xFF; assert_different_registers(src, dst, cnt, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5); Label Lloop, Lslow; // Check if cnt >= 8 (= 16 bytes) - lis(tmp1, 0xFF); // tmp1 = 0x00FF00FF00FF00FF + lis(tmp1, byte_mask); // tmp1 = 0x00FF00FF00FF00FF (non ascii case) srwi_(tmp2, cnt, 3); beq(CCR0, Lslow); - ori(tmp1, tmp1, 0xFF); + ori(tmp1, tmp1, byte_mask); rldimi(tmp1, tmp1, 32, 0); mtctr(tmp2); @@ -67,7 +68,7 @@ rldimi(tmp4, tmp4, 2*8, 2*8); // _4_6_7_7 andc_(tmp0, tmp0, tmp1); - bne(CCR0, Lfailure); // Not latin1. + bne(CCR0, Lfailure); // Not latin1/ascii. addi(src, src, 16); rlwimi(tmp3, tmp2, 0*8, 24, 31);// _____1_3 @@ -87,20 +88,49 @@ } // Compress char[] to byte[]. cnt must be positive int. -void C2_MacroAssembler::string_compress(Register src, Register dst, Register cnt, Register tmp, Label& Lfailure) { +void C2_MacroAssembler::string_compress(Register src, Register dst, Register cnt, Register tmp, + Label& Lfailure, bool ascii) { + const int byte_mask = ascii ? 0x7F : 0xFF; Label Lloop; mtctr(cnt); bind(Lloop); lhz(tmp, 0, src); - cmplwi(CCR0, tmp, 0xff); - bgt(CCR0, Lfailure); // Not latin1. + cmplwi(CCR0, tmp, byte_mask); + bgt(CCR0, Lfailure); // Not latin1/ascii. addi(src, src, 2); stb(tmp, 0, dst); addi(dst, dst, 1); bdnz(Lloop); } +void C2_MacroAssembler::encode_iso_array(Register src, Register dst, Register len, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, + Register result, bool ascii) { + Label Lslow, Lfailure1, Lfailure2, Ldone; + + string_compress_16(src, dst, len, tmp1, tmp2, tmp3, tmp4, tmp5, Lfailure1, ascii); + rldicl_(result, len, 0, 64-3); // Remaining characters. + beq(CCR0, Ldone); + bind(Lslow); + string_compress(src, dst, result, tmp2, Lfailure2, ascii); + li(result, 0); + b(Ldone); + + bind(Lfailure1); + mr(result, len); + mfctr(tmp1); + rldimi_(result, tmp1, 3, 0); // Remaining characters. + beq(CCR0, Ldone); + b(Lslow); + + bind(Lfailure2); + mfctr(result); // Remaining characters. + + bind(Ldone); + subf(result, result, len); +} + // Inflate byte[] to char[] by inflating 16 bytes at once. void C2_MacroAssembler::string_inflate_16(Register src, Register dst, Register cnt, Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -32,10 +32,16 @@ // Compress char[] to byte[] by compressing 16 bytes at once. void string_compress_16(Register src, Register dst, Register cnt, Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, - Label& Lfailure); + Label& Lfailure, bool ascii = false); // Compress char[] to byte[]. cnt must be positive int. - void string_compress(Register src, Register dst, Register cnt, Register tmp, Label& Lfailure); + void string_compress(Register src, Register dst, Register cnt, Register tmp, + Label& Lfailure, bool ascii = false); + + // Encode UTF16 to ISO_8859_1 or ASCII. Return len on success or position of first mismatch. + void encode_iso_array(Register src, Register dst, Register len, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, + Register result, bool ascii); // Inflate byte[] to char[] by inflating 16 bytes at once. void string_inflate_16(Register src, Register dst, Register cnt, diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/frame_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/frame_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/frame_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/frame_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -294,9 +294,57 @@ } bool frame::is_interpreted_frame_valid(JavaThread* thread) const { - // Is there anything to do? assert(is_interpreted_frame(), "Not an interpreted frame"); - return true; + // These are reasonable sanity checks + if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) { + return false; + } + if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) { + return false; + } + int min_frame_slots = (abi_minframe_size + ijava_state_size) / sizeof(intptr_t); + if (fp() - min_frame_slots < sp()) { + return false; + } + // These are hacks to keep us out of trouble. + // The problem with these is that they mask other problems + if (fp() <= sp()) { // this attempts to deal with unsigned comparison above + return false; + } + + // do some validation of frame elements + + // first the method + + Method* m = *interpreter_frame_method_addr(); + + // validate the method we'd find in this potential sender + if (!Method::is_valid_method(m)) return false; + + // stack frames shouldn't be much larger than max_stack elements + // this test requires the use of unextended_sp which is the sp as seen by + // the current frame, and not sp which is the "raw" pc which could point + // further because of local variables of the callee method inserted after + // method arguments + if (fp() - unextended_sp() > 1024 + m->max_stack()*Interpreter::stackElementSize) { + return false; + } + + // validate bci/bcx + + address bcp = interpreter_frame_bcp(); + if (m->validate_bci_from_bcp(bcp) < 0) { + return false; + } + + // validate constantPoolCache* + ConstantPoolCache* cp = *interpreter_frame_cache_addr(); + if (MetaspaceObj::is_valid(cp) == false) return false; + + // validate locals + + address locals = (address) *interpreter_frame_locals_addr(); + return thread->is_in_stack_range_incl(locals, (address)fp()); } BasicType frame::interpreter_frame_result(oop* oop_result, jvalue* value_result) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/matcher_ppc.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/matcher_ppc.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/matcher_ppc.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/matcher_ppc.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -157,6 +157,6 @@ } // Implements a variant of EncodeISOArrayNode that encode ASCII only - static const bool supports_encode_ascii_array = false; + static const bool supports_encode_ascii_array = true; #endif // CPU_PPC_MATCHER_PPC_HPP diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/ppc.ad openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/ppc.ad --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/ppc.ad 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/ppc.ad 2022-04-19 19:55:43.000000000 +0000 @@ -12798,30 +12798,26 @@ effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, USE_KILL src, USE_KILL dst, KILL ctr, KILL cr0); ins_cost(300); - format %{ "Encode array $src,$dst,$len -> $result \t// KILL $tmp1, $tmp2, $tmp3, $tmp4, $tmp5" %} + format %{ "Encode iso array $src,$dst,$len -> $result \t// KILL $tmp1, $tmp2, $tmp3, $tmp4, $tmp5" %} ins_encode %{ - Label Lslow, Lfailure1, Lfailure2, Ldone; - __ string_compress_16($src$$Register, $dst$$Register, $len$$Register, $tmp1$$Register, - $tmp2$$Register, $tmp3$$Register, $tmp4$$Register, $tmp5$$Register, Lfailure1); - __ rldicl_($result$$Register, $len$$Register, 0, 64-3); // Remaining characters. - __ beq(CCR0, Ldone); - __ bind(Lslow); - __ string_compress($src$$Register, $dst$$Register, $result$$Register, $tmp2$$Register, Lfailure2); - __ li($result$$Register, 0); - __ b(Ldone); - - __ bind(Lfailure1); - __ mr($result$$Register, $len$$Register); - __ mfctr($tmp1$$Register); - __ rldimi_($result$$Register, $tmp1$$Register, 3, 0); // Remaining characters. - __ beq(CCR0, Ldone); - __ b(Lslow); - - __ bind(Lfailure2); - __ mfctr($result$$Register); // Remaining characters. + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$Register, $tmp2$$Register, + $tmp3$$Register, $tmp4$$Register, $tmp5$$Register, $result$$Register, false); + %} + ins_pipe(pipe_class_default); +%} - __ bind(Ldone); - __ subf($result$$Register, $result$$Register, $len$$Register); +// encode char[] to byte[] in ASCII +instruct encode_ascii_array(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst result, iRegLdst tmp1, + iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, iRegLdst tmp5, regCTR ctr, flagsRegCR0 cr0) %{ + predicate(((EncodeISOArrayNode*)n)->is_ascii()); + match(Set result (EncodeISOArray src (Binary dst len))); + effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, + USE_KILL src, USE_KILL dst, KILL ctr, KILL cr0); + ins_cost(300); + format %{ "Encode ascii array $src,$dst,$len -> $result \t// KILL $tmp1, $tmp2, $tmp3, $tmp4, $tmp5" %} + ins_encode %{ + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$Register, $tmp2$$Register, + $tmp3$$Register, $tmp4$$Register, $tmp5$$Register, $result$$Register, true); %} ins_pipe(pipe_class_default); %} diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -74,8 +74,8 @@ push_frame(frame_size_in_bytes); } -void C1_MacroAssembler::verified_entry() { - if (C1Breakpoint) z_illtrap(0xC1); +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { + if (breakAtEntry) z_illtrap(0xC1); } void C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Label& slow_case) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/s390/frame_s390.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/s390/frame_s390.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/s390/frame_s390.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/s390/frame_s390.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2019 SAP SE. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -117,8 +117,8 @@ return false; } - z_abi_160* sender_abi = (z_abi_160*) fp; - intptr_t* sender_sp = (intptr_t*) sender_abi->callers_sp; + z_abi_16* sender_abi = (z_abi_16*)fp; + intptr_t* sender_sp = (intptr_t*) fp; address sender_pc = (address) sender_abi->return_pc; // We must always be able to find a recognizable pc. @@ -142,7 +142,7 @@ // sender_fp must be within the stack and above (but not // equal) current frame's fp. if (!thread->is_in_stack_range_excl(sender_fp, fp)) { - return false; + return false; } // If the potential sender is the interpreter then we can do some more checking. @@ -298,9 +298,55 @@ } bool frame::is_interpreted_frame_valid(JavaThread* thread) const { - // Is there anything to do? assert(is_interpreted_frame(), "Not an interpreted frame"); - return true; + // These are reasonable sanity checks + if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) { + return false; + } + if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) { + return false; + } + int min_frame_slots = (z_abi_16_size + z_ijava_state_size) / sizeof(intptr_t); + if (fp() - min_frame_slots < sp()) { + return false; + } + // These are hacks to keep us out of trouble. + // The problem with these is that they mask other problems + if (fp() <= sp()) { // this attempts to deal with unsigned comparison above + return false; + } + + // do some validation of frame elements + + // first the method + // Need to use "unchecked" versions to avoid "z_istate_magic_number" assertion. + Method* m = (Method*)(ijava_state_unchecked()->method); + + // validate the method we'd find in this potential sender + if (!Method::is_valid_method(m)) return false; + + // stack frames shouldn't be much larger than max_stack elements + // this test requires the use of unextended_sp which is the sp as seen by + // the current frame, and not sp which is the "raw" pc which could point + // further because of local variables of the callee method inserted after + // method arguments + if (fp() - unextended_sp() > 1024 + m->max_stack()*Interpreter::stackElementSize) { + return false; + } + + // validate bci/bcx + address bcp = (address)(ijava_state_unchecked()->bcp); + if (m->validate_bci_from_bcp(bcp) < 0) { + return false; + } + + // validate constantPoolCache* + ConstantPoolCache* cp = (ConstantPoolCache*)(ijava_state_unchecked()->cpoolCache); + if (MetaspaceObj::is_valid(cp) == false) return false; + + // validate locals + address locals = (address)(ijava_state_unchecked()->locals); + return thread->is_in_stack_range_incl(locals, (address)fp()); } BasicType frame::interpreter_frame_result(oop* oop_result, jvalue* value_result) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/assembler_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/assembler_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/assembler_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/assembler_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -187,7 +187,7 @@ // Address. An index of 4 (rsp) corresponds to having no index, so convert // that to noreg for the Address constructor. Address Address::make_raw(int base, int index, int scale, int disp, relocInfo::relocType disp_reloc) { - RelocationHolder rspec; + RelocationHolder rspec = RelocationHolder::none; if (disp_reloc != relocInfo::none) { rspec = Relocation::spec_simple(disp_reloc); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -354,18 +354,18 @@ } -void C1_MacroAssembler::verified_entry() { - if (C1Breakpoint || VerifyFPU) { +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { + if (breakAtEntry || VerifyFPU) { // Verified Entry first instruction should be 5 bytes long for correct // patching by patch_verified_entry(). // - // C1Breakpoint and VerifyFPU have one byte first instruction. + // Breakpoint and VerifyFPU have one byte first instruction. // Also first instruction will be one byte "push(rbp)" if stack banging // code is not generated (see build_frame() above). // For all these cases generate long instruction first. fat_nop(); } - if (C1Breakpoint)int3(); + if (breakAtEntry) int3(); // build frame IA32_ONLY( verify_FPU(0, "method_entry"); ) } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -602,8 +602,13 @@ // Unconditionally set box->_displaced_header = markWord::unused_mark(). // Without cast to int32_t this style of movptr will destroy r10 which is typically obj. movptr(Address(boxReg, 0), (int32_t)intptr_t(markWord::unused_mark().value())); - // Intentional fall-through into DONE_LABEL ... // Propagate ICC.ZF from CAS above into DONE_LABEL. + jcc(Assembler::equal, DONE_LABEL); // CAS above succeeded; propagate ZF = 1 (success) + + cmpptr(r15_thread, rax); // Check if we are already the owner (recursive lock) + jcc(Assembler::notEqual, DONE_LABEL); // If not recursive, ZF = 0 at this point (fail) + incq(Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); + xorq(rax, rax); // Set ZF = 1 (success) for recursive lock, denoting locking success #endif // _LP64 #if INCLUDE_RTM_OPT } // use_rtm() @@ -705,10 +710,6 @@ // Refer to the comments in synchronizer.cpp for how we might encode extra // state in _succ so we can avoid fetching EntryList|cxq. // - // I'd like to add more cases in fast_lock() and fast_unlock() -- - // such as recursive enter and exit -- but we have to be wary of - // I$ bloat, T$ effects and BP$ effects. - // // If there's no contention try a 1-0 exit. That is, exit without // a costly MEMBAR or CAS. See synchronizer.cpp for details on how // we detect and recover from the race that the 1-0 exit admits. @@ -756,9 +757,16 @@ bind (CheckSucc); #else // _LP64 // It's inflated - xorptr(boxReg, boxReg); - orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); - jccb (Assembler::notZero, DONE_LABEL); + Label LNotRecursive, LSuccess, LGoSlowPath; + + cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 0); + jccb(Assembler::equal, LNotRecursive); + + // Recursive inflated unlock + decq(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); + jmpb(LSuccess); + + bind(LNotRecursive); movptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq))); orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList))); jccb (Assembler::notZero, CheckSucc); @@ -767,7 +775,6 @@ jmpb (DONE_LABEL); // Try to avoid passing control into the slow_path ... - Label LSuccess, LGoSlowPath ; bind (CheckSucc); // The following optional optimization can be elided if necessary diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -155,14 +155,15 @@ method = JVMCIENV->asMethod(hotspot_method); } #endif + NativeCall* call = NULL; switch (_next_call_type) { case INLINE_INVOKE: - break; + return; case INVOKEVIRTUAL: case INVOKEINTERFACE: { assert(method == NULL || !method->is_static(), "cannot call static method with invokeinterface"); - NativeCall* call = nativeCall_at(_instructions->start() + pc_offset); + call = nativeCall_at(_instructions->start() + pc_offset); call->set_destination(SharedRuntime::get_resolve_virtual_call_stub()); _instructions->relocate(call->instruction_address(), virtual_call_Relocation::spec(_invoke_mark_pc), @@ -172,7 +173,7 @@ case INVOKESTATIC: { assert(method == NULL || method->is_static(), "cannot call non-static method with invokestatic"); - NativeCall* call = nativeCall_at(_instructions->start() + pc_offset); + call = nativeCall_at(_instructions->start() + pc_offset); call->set_destination(SharedRuntime::get_resolve_static_call_stub()); _instructions->relocate(call->instruction_address(), relocInfo::static_call_type, Assembler::call32_operand); @@ -180,15 +181,18 @@ } case INVOKESPECIAL: { assert(method == NULL || !method->is_static(), "cannot call static method with invokespecial"); - NativeCall* call = nativeCall_at(_instructions->start() + pc_offset); + call = nativeCall_at(_instructions->start() + pc_offset); call->set_destination(SharedRuntime::get_resolve_opt_virtual_call_stub()); _instructions->relocate(call->instruction_address(), relocInfo::opt_virtual_call_type, Assembler::call32_operand); break; } default: - JVMCI_ERROR("invalid _next_call_type value"); - break; + JVMCI_ERROR("invalid _next_call_type value: %d", _next_call_type); + return; + } + if (!call->is_displacement_aligned()) { + JVMCI_ERROR("unaligned displacement for call at offset %d", pc_offset); } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/nativeInst_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/nativeInst_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/nativeInst_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/nativeInst_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -260,6 +260,9 @@ } +bool NativeCall::is_displacement_aligned() { + return (uintptr_t) displacement_address() % 4 == 0; +} // Similar to replace_mt_safe, but just changes the destination. The // important thing is that free-running threads are able to execute this @@ -282,8 +285,7 @@ CompiledICLocker::is_safe(instruction_address()), "concurrent code patching"); // Both C1 and C2 should now be generating code which aligns the patched address // to be within a single cache line. - bool is_aligned = ((uintptr_t)displacement_address() + 0) / cache_line_size == - ((uintptr_t)displacement_address() + 3) / cache_line_size; + bool is_aligned = is_displacement_aligned(); guarantee(is_aligned, "destination must be aligned"); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/nativeInst_x86.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/nativeInst_x86.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/nativeInst_x86.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/nativeInst_x86.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -160,8 +160,6 @@ return_address_offset = 5 }; - enum { cache_line_size = BytesPerWord }; // conservative estimate! - address instruction_address() const { return addr_at(instruction_offset); } address next_instruction_address() const { return addr_at(return_address_offset); } int displacement() const { return (jint) int_at(displacement_offset); } @@ -175,9 +173,11 @@ #endif // AMD64 set_int_at(displacement_offset, dest - return_address()); } + // Returns whether the 4-byte displacement operand is 4-byte aligned. + bool is_displacement_aligned(); void set_destination_mt_safe(address dest); - void verify_alignment() { assert((intptr_t)addr_at(displacement_offset) % BytesPerInt == 0, "must be aligned"); } + void verify_alignment() { assert(is_displacement_aligned(), "displacement of call is not aligned"); } void verify(); void print(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/vmreg_x86.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/vmreg_x86.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/vmreg_x86.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/vmreg_x86.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -90,7 +90,13 @@ #ifndef AMD64 if (is_Register()) return true; #endif // AMD64 - return is_even(value()); + // Do not use is_XMMRegister() here as it depends on the UseAVX setting. + if (value() >= ConcreteRegisterImpl::max_fpr && value() < ConcreteRegisterImpl::max_xmm) { + int base = value() - ConcreteRegisterImpl::max_fpr; + return base % XMMRegisterImpl::max_slots_per_register == 0; + } else { + return is_even(value()); // General, float, and K registers are all two slots wide + } } #endif // CPU_X86_VMREG_X86_HPP diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/x86.ad openjdk-17-17.0.3+7/src/hotspot/cpu/x86/x86.ad --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/x86.ad 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/x86.ad 2022-04-19 19:55:43.000000000 +0000 @@ -1621,6 +1621,21 @@ return false; } break; + case Op_SqrtF: + if (UseSSE < 1) { + return false; + } + break; + case Op_SqrtD: +#ifdef _LP64 + if (UseSSE < 2) { + return false; + } +#else + // x86_32.ad has a special match rule for SqrtD. + // Together with common x86 rules, this handles all UseSSE cases. +#endif + break; } return true; // Match rules are supported by default. } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/x86_32.ad openjdk-17-17.0.3+7/src/hotspot/cpu/x86/x86_32.ad --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/x86_32.ad 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/x86_32.ad 2022-04-19 19:55:43.000000000 +0000 @@ -755,6 +755,7 @@ assert(UseSSE < 2, "shouldn't be used in SSE2+ mode"); return rc_float; } + if (r->is_KRegister()) return rc_kreg; assert(r->is_XMMRegister(), "must be"); return rc_xmm; } @@ -1249,26 +1250,6 @@ return size; } - assert( size > 0, "missed a case" ); - - // -------------------------------------------------------------------- - // Check for second bits still needing moving. - if( src_second == dst_second ) - return size; // Self copy; no move - assert( src_second_rc != rc_bad && dst_second_rc != rc_bad, "src_second & dst_second cannot be Bad" ); - - // Check for second word int-int move - if( src_second_rc == rc_int && dst_second_rc == rc_int ) - return impl_mov_helper(cbuf,do_size,src_second,dst_second,size, st); - - // Check for second word integer store - if( src_second_rc == rc_int && dst_second_rc == rc_stack ) - return impl_helper(cbuf,do_size,false,ra_->reg2offset(dst_second),src_second,0x89,"MOV ",size, st); - - // Check for second word integer load - if( dst_second_rc == rc_int && src_second_rc == rc_stack ) - return impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_second),dst_second,0x8B,"MOV ",size, st); - // AVX-512 opmask specific spilling. if (src_first_rc == rc_stack && dst_first_rc == rc_kreg) { assert((src_first & 1) == 0 && src_first + 1 == src_second, "invalid register pair"); @@ -1306,6 +1287,26 @@ return 0; } + assert( size > 0, "missed a case" ); + + // -------------------------------------------------------------------- + // Check for second bits still needing moving. + if( src_second == dst_second ) + return size; // Self copy; no move + assert( src_second_rc != rc_bad && dst_second_rc != rc_bad, "src_second & dst_second cannot be Bad" ); + + // Check for second word int-int move + if( src_second_rc == rc_int && dst_second_rc == rc_int ) + return impl_mov_helper(cbuf,do_size,src_second,dst_second,size, st); + + // Check for second word integer store + if( src_second_rc == rc_int && dst_second_rc == rc_stack ) + return impl_helper(cbuf,do_size,false,ra_->reg2offset(dst_second),src_second,0x89,"MOV ",size, st); + + // Check for second word integer load + if( dst_second_rc == rc_int && src_second_rc == rc_stack ) + return impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_second),dst_second,0x8B,"MOV ",size, st); + Unimplemented(); return 0; // Mute compiler } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/zero/globals_zero.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/zero/globals_zero.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/zero/globals_zero.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/zero/globals_zero.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -70,8 +70,7 @@ define_pd_global(bool, PreserveFramePointer, false); -// No performance work done here yet. -define_pd_global(bool, CompactStrings, false); +define_pd_global(bool, CompactStrings, true); #define ARCH_FLAGS(develop, \ product, \ diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/zero/vm_version_zero.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/zero/vm_version_zero.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/zero/vm_version_zero.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/zero/vm_version_zero.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -52,4 +52,8 @@ // Not implemented UNSUPPORTED_OPTION(CriticalJNINatives); + UNSUPPORTED_OPTION(UseCompiler); +#ifdef ASSERT + UNSUPPORTED_OPTION(CountCompiledCalls); +#endif } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -68,14 +68,6 @@ ZeroInterpreterGenerator g(_code); if (PrintInterpreter) print(); } - - // Allow c++ interpreter to do one initialization now that switches are set, etc. - BytecodeInterpreter start_msg(BytecodeInterpreter::initialize); - if (JvmtiExport::can_post_interpreter_events()) { - BytecodeInterpreter::run(&start_msg); - } else { - BytecodeInterpreter::run(&start_msg); - } } void ZeroInterpreter::invoke_method(Method* method, address entry_point, TRAPS) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/bsd/decoder_machO.cpp openjdk-17-17.0.3+7/src/hotspot/os/bsd/decoder_machO.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/bsd/decoder_machO.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/bsd/decoder_machO.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -52,6 +52,12 @@ bool MachODecoder::decode(address addr, char *buf, int buflen, int *offset, const void *mach_base) { + if (addr == (address)(intptr_t)-1) { + // dladdr() in macOS12/Monterey returns success for -1, but that addr value + // won't work in this function. Should have been handled by the caller. + ShouldNotReachHere(); + return false; + } struct symtab_command * symt = (struct symtab_command *) mach_find_command((struct mach_header_64 *)mach_base, LC_SYMTAB); if (symt == NULL) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/bsd/os_bsd.cpp openjdk-17-17.0.3+7/src/hotspot/os/bsd/os_bsd.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/bsd/os_bsd.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/bsd/os_bsd.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -903,6 +903,17 @@ const char* os::dll_file_extension() { return JNI_LIB_SUFFIX; } +static int local_dladdr(const void* addr, Dl_info* info) { +#ifdef __APPLE__ + if (addr == (void*)-1) { + // dladdr() in macOS12/Monterey returns success for -1, but that addr + // value should not be allowed to work to avoid confusion. + return 0; + } +#endif + return dladdr(addr, info); +} + // This must be hard coded because it's the system's temporary // directory not the java application's temp directory, ala java.io.tmpdir. #ifdef __APPLE__ @@ -942,9 +953,6 @@ return false; } - -#define MACH_MAXSYMLEN 256 - bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int *offset, bool demangle) { @@ -952,9 +960,8 @@ assert(buf != NULL, "sanity check"); Dl_info dlinfo; - char localbuf[MACH_MAXSYMLEN]; - if (dladdr((void*)addr, &dlinfo) != 0) { + if (local_dladdr((void*)addr, &dlinfo) != 0) { // see if we have a matching symbol if (dlinfo.dli_saddr != NULL && dlinfo.dli_sname != NULL) { if (!(demangle && Decoder::demangle(dlinfo.dli_sname, buf, buflen))) { @@ -963,6 +970,14 @@ if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; return true; } + +#ifndef __APPLE__ + // The 6-parameter Decoder::decode() function is not implemented on macOS. + // The Mach-O binary format does not contain a "list of files" with address + // ranges like ELF. That makes sense since Mach-O can contain binaries for + // than one instruction set so there can be more than one address range for + // each "file". + // no matching symbol so try for just file info if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != NULL) { if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase), @@ -971,6 +986,10 @@ } } +#else // __APPLE__ + #define MACH_MAXSYMLEN 256 + + char localbuf[MACH_MAXSYMLEN]; // Handle non-dynamic manually: if (dlinfo.dli_fbase != NULL && Decoder::decode(addr, localbuf, MACH_MAXSYMLEN, offset, @@ -980,6 +999,9 @@ } return true; } + + #undef MACH_MAXSYMLEN +#endif // __APPLE__ } buf[0] = '\0'; if (offset != NULL) *offset = -1; @@ -994,7 +1016,7 @@ Dl_info dlinfo; - if (dladdr((void*)addr, &dlinfo) != 0) { + if (local_dladdr((void*)addr, &dlinfo) != 0) { if (dlinfo.dli_fname != NULL) { jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); } @@ -1392,13 +1414,17 @@ strncpy(machine, "", sizeof(machine)); } - const char* emulated = ""; #if defined(__APPLE__) && !defined(ZERO) if (VM_Version::is_cpu_emulated()) { - emulated = " (EMULATED)"; + snprintf(buf, buflen, "\"%s\" %s (EMULATED) %d MHz", model, machine, mhz); + } else { + NOT_AARCH64(snprintf(buf, buflen, "\"%s\" %s %d MHz", model, machine, mhz)); + // aarch64 CPU doesn't report its speed + AARCH64_ONLY(snprintf(buf, buflen, "\"%s\" %s", model, machine)); } +#else + snprintf(buf, buflen, "\"%s\" %s %d MHz", model, machine, mhz); #endif - snprintf(buf, buflen, "\"%s\" %s%s %d MHz", model, machine, emulated, mhz); } void os::print_memory_info(outputStream* st) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupSubsystem_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupSubsystem_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupSubsystem_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupSubsystem_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,11 +34,15 @@ #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" +// controller names have to match the *_IDX indices +static const char* cg_controller_name[] = { "cpu", "cpuset", "cpuacct", "memory", "pids" }; + CgroupSubsystem* CgroupSubsystemFactory::create() { CgroupV1MemoryController* memory = NULL; CgroupV1Controller* cpuset = NULL; CgroupV1Controller* cpu = NULL; CgroupV1Controller* cpuacct = NULL; + CgroupV1Controller* pids = NULL; CgroupInfo cg_infos[CG_INFO_LENGTH]; u1 cg_type_flags = INVALID_CGROUPS_GENERIC; const char* proc_cgroups = "/proc/cgroups"; @@ -93,22 +97,29 @@ assert(is_cgroup_v1(&cg_type_flags), "Cgroup v1 expected"); for (int i = 0; i < CG_INFO_LENGTH; i++) { CgroupInfo info = cg_infos[i]; - if (strcmp(info._name, "memory") == 0) { - memory = new CgroupV1MemoryController(info._root_mount_path, info._mount_path); - memory->set_subsystem_path(info._cgroup_path); - } else if (strcmp(info._name, "cpuset") == 0) { - cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path); - cpuset->set_subsystem_path(info._cgroup_path); - } else if (strcmp(info._name, "cpu") == 0) { - cpu = new CgroupV1Controller(info._root_mount_path, info._mount_path); - cpu->set_subsystem_path(info._cgroup_path); - } else if (strcmp(info._name, "cpuacct") == 0) { - cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path); - cpuacct->set_subsystem_path(info._cgroup_path); + if (info._data_complete) { // pids controller might have incomplete data + if (strcmp(info._name, "memory") == 0) { + memory = new CgroupV1MemoryController(info._root_mount_path, info._mount_path); + memory->set_subsystem_path(info._cgroup_path); + } else if (strcmp(info._name, "cpuset") == 0) { + cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path); + cpuset->set_subsystem_path(info._cgroup_path); + } else if (strcmp(info._name, "cpu") == 0) { + cpu = new CgroupV1Controller(info._root_mount_path, info._mount_path); + cpu->set_subsystem_path(info._cgroup_path); + } else if (strcmp(info._name, "cpuacct") == 0) { + cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path); + cpuacct->set_subsystem_path(info._cgroup_path); + } else if (strcmp(info._name, "pids") == 0) { + pids = new CgroupV1Controller(info._root_mount_path, info._mount_path); + pids->set_subsystem_path(info._cgroup_path); + } + } else { + log_debug(os, container)("CgroupInfo for %s not complete", cg_controller_name[i]); } } cleanup(cg_infos); - return new CgroupV1Subsystem(cpuset, cpu, cpuacct, memory); + return new CgroupV1Subsystem(cpuset, cpu, cpuacct, pids, memory); } bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, @@ -122,9 +133,10 @@ char buf[MAXPATHLEN+1]; char *p; bool is_cgroupsV2; - // true iff all controllers, memory, cpu, cpuset, cpuacct are enabled + // true iff all required controllers, memory, cpu, cpuset, cpuacct are enabled // at the kernel level. - bool all_controllers_enabled; + // pids might not be enabled on older Linux distros (SLES 12.1, RHEL 7.1) + bool all_required_controllers_enabled; /* * Read /proc/cgroups so as to be able to distinguish cgroups v2 vs cgroups v1. @@ -136,10 +148,9 @@ */ cgroups = fopen(proc_cgroups, "r"); if (cgroups == NULL) { - log_debug(os, container)("Can't open %s, %s", - proc_cgroups, os::strerror(errno)); - *flags = INVALID_CGROUPS_GENERIC; - return false; + log_debug(os, container)("Can't open %s, %s", proc_cgroups, os::strerror(errno)); + *flags = INVALID_CGROUPS_GENERIC; + return false; } while ((p = fgets(buf, MAXPATHLEN, cgroups)) != NULL) { @@ -167,19 +178,30 @@ cg_infos[CPUACCT_IDX]._name = os::strdup(name); cg_infos[CPUACCT_IDX]._hierarchy_id = hierarchy_id; cg_infos[CPUACCT_IDX]._enabled = (enabled == 1); + } else if (strcmp(name, "pids") == 0) { + log_debug(os, container)("Detected optional pids controller entry in %s", proc_cgroups); + cg_infos[PIDS_IDX]._name = os::strdup(name); + cg_infos[PIDS_IDX]._hierarchy_id = hierarchy_id; + cg_infos[PIDS_IDX]._enabled = (enabled == 1); } } fclose(cgroups); is_cgroupsV2 = true; - all_controllers_enabled = true; + all_required_controllers_enabled = true; for (int i = 0; i < CG_INFO_LENGTH; i++) { - is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0; - all_controllers_enabled = all_controllers_enabled && cg_infos[i]._enabled; + // pids controller is optional. All other controllers are required + if (i != PIDS_IDX) { + is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0; + all_required_controllers_enabled = all_required_controllers_enabled && cg_infos[i]._enabled; + } + if (log_is_enabled(Debug, os, container) && !cg_infos[i]._enabled) { + log_debug(os, container)("controller %s is not enabled\n", cg_controller_name[i]); + } } - if (!all_controllers_enabled) { - // one or more controllers disabled, disable container support + if (!all_required_controllers_enabled) { + // one or more required controllers disabled, disable container support log_debug(os, container)("One or more required controllers disabled at kernel level."); cleanup(cg_infos); *flags = INVALID_CGROUPS_GENERIC; @@ -220,17 +242,21 @@ while (!is_cgroupsV2 && (token = strsep(&controllers, ",")) != NULL) { if (strcmp(token, "memory") == 0) { - assert(hierarchy_id == cg_infos[MEMORY_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch"); + assert(hierarchy_id == cg_infos[MEMORY_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for memory"); cg_infos[MEMORY_IDX]._cgroup_path = os::strdup(cgroup_path); } else if (strcmp(token, "cpuset") == 0) { - assert(hierarchy_id == cg_infos[CPUSET_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch"); + assert(hierarchy_id == cg_infos[CPUSET_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpuset"); cg_infos[CPUSET_IDX]._cgroup_path = os::strdup(cgroup_path); } else if (strcmp(token, "cpu") == 0) { - assert(hierarchy_id == cg_infos[CPU_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch"); + assert(hierarchy_id == cg_infos[CPU_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpu"); cg_infos[CPU_IDX]._cgroup_path = os::strdup(cgroup_path); } else if (strcmp(token, "cpuacct") == 0) { - assert(hierarchy_id == cg_infos[CPUACCT_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch"); + assert(hierarchy_id == cg_infos[CPUACCT_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpuacc"); cg_infos[CPUACCT_IDX]._cgroup_path = os::strdup(cgroup_path); + } else if (strcmp(token, "pids") == 0) { + assert(hierarchy_id == cg_infos[PIDS_IDX]._hierarchy_id, "/proc/cgroups (%d) and /proc/self/cgroup (%d) hierarchy mismatch for pids", + cg_infos[PIDS_IDX]._hierarchy_id, hierarchy_id); + cg_infos[PIDS_IDX]._cgroup_path = os::strdup(cgroup_path); } } if (is_cgroupsV2) { @@ -281,13 +307,15 @@ /* Cgroup v1 relevant info * - * Find the cgroup mount point for memory, cpuset, cpu, cpuacct + * Find the cgroup mount point for memory, cpuset, cpu, cpuacct, pids * * Example for docker: * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory * * Example for host: * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory + * + * 44 31 0:39 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:23 - cgroup cgroup rw,pids */ if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- %s %*s %s", tmproot, tmpmount, tmp_fs_type, tmpcgroups) == 4) { if (strcmp("cgroup", tmp_fs_type) != 0) { @@ -333,6 +361,12 @@ cg_infos[CPUACCT_IDX]._mount_path = os::strdup(tmpmount); cg_infos[CPUACCT_IDX]._root_mount_path = os::strdup(tmproot); cg_infos[CPUACCT_IDX]._data_complete = true; + } else if (strcmp(token, "pids") == 0) { + any_cgroup_mounts_found = true; + assert(cg_infos[PIDS_IDX]._mount_path == NULL, "stomping of _mount_path"); + cg_infos[PIDS_IDX]._mount_path = os::strdup(tmpmount); + cg_infos[PIDS_IDX]._root_mount_path = os::strdup(tmproot); + cg_infos[PIDS_IDX]._data_complete = true; } } } @@ -387,10 +421,13 @@ *flags = INVALID_CGROUPS_V1; return false; } + if (log_is_enabled(Debug, os, container) && !cg_infos[PIDS_IDX]._data_complete) { + log_debug(os, container)("Optional cgroup v1 pids subsystem not found"); + // keep the other controller info, pids is optional + } // Cgroups v1 case, we have all the info we need. *flags = CGROUPS_V1; return true; - }; void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) { @@ -514,3 +551,22 @@ memory_limit->set_value(mem_limit, OSCONTAINER_CACHE_TIMEOUT); return mem_limit; } + +jlong CgroupSubsystem::limit_from_str(char* limit_str) { + if (limit_str == NULL) { + return OSCONTAINER_ERROR; + } + // Unlimited memory in cgroups is the literal string 'max' for + // some controllers, for example the pids controller. + if (strcmp("max", limit_str) == 0) { + os::free(limit_str); + return (jlong)-1; + } + julong limit; + if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) { + os::free(limit_str); + return OSCONTAINER_ERROR; + } + os::free(limit_str); + return (jlong)limit; +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupSubsystem_linux.hpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupSubsystem_linux.hpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupSubsystem_linux.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupSubsystem_linux.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,12 +61,13 @@ #define INVALID_CGROUPS_NO_MOUNT 5 #define INVALID_CGROUPS_GENERIC 6 -// Four controllers: cpu, cpuset, cpuacct, memory -#define CG_INFO_LENGTH 4 +// Five controllers: cpu, cpuset, cpuacct, memory, pids +#define CG_INFO_LENGTH 5 #define CPUSET_IDX 0 #define CPU_IDX 1 #define CPUACCT_IDX 2 #define MEMORY_IDX 3 +#define PIDS_IDX 4 typedef char * cptr; @@ -156,8 +157,10 @@ NULL, \ scan_fmt, \ &variable); \ - if (err != 0) \ + if (err != 0) { \ + log_trace(os, container)(logstring, (return_type) OSCONTAINER_ERROR); \ return (return_type) OSCONTAINER_ERROR; \ + } \ \ log_trace(os, container)(logstring, variable); \ } @@ -238,10 +241,13 @@ public: jlong memory_limit_in_bytes(); int active_processor_count(); + jlong limit_from_str(char* limit_str); virtual int cpu_quota() = 0; virtual int cpu_period() = 0; virtual int cpu_shares() = 0; + virtual jlong pids_max() = 0; + virtual jlong pids_current() = 0; virtual jlong memory_usage_in_bytes() = 0; virtual jlong memory_and_swap_limit_in_bytes() = 0; virtual jlong memory_soft_limit_in_bytes() = 0; diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -241,3 +241,43 @@ return shares; } + + +char* CgroupV1Subsystem::pids_max_val() { + GET_CONTAINER_INFO_CPTR(cptr, _pids, "/pids.max", + "Maximum number of tasks is: %s", "%s %*d", pidsmax, 1024); + if (pidsmax == NULL) { + return NULL; + } + return os::strdup(pidsmax); +} + +/* pids_max + * + * Return the maximum number of tasks available to the process + * + * return: + * maximum number of tasks + * -1 for unlimited + * OSCONTAINER_ERROR for not supported + */ +jlong CgroupV1Subsystem::pids_max() { + if (_pids == NULL) return OSCONTAINER_ERROR; + char * pidsmax_str = pids_max_val(); + return limit_from_str(pidsmax_str); +} + +/* pids_current + * + * The number of tasks currently in the cgroup (and its descendants) of the process + * + * return: + * current number of tasks + * OSCONTAINER_ERROR for not supported + */ +jlong CgroupV1Subsystem::pids_current() { + if (_pids == NULL) return OSCONTAINER_ERROR; + GET_CONTAINER_INFO(jlong, _pids, "/pids.current", + "Current number of tasks is: " JLONG_FORMAT, JLONG_FORMAT, pids_current); + return pids_current; +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -87,6 +87,9 @@ int cpu_shares(); + jlong pids_max(); + jlong pids_current(); + const char * container_type() { return "cgroupv1"; } @@ -101,15 +104,20 @@ CgroupV1Controller* _cpuset = NULL; CachingCgroupController* _cpu = NULL; CgroupV1Controller* _cpuacct = NULL; + CgroupV1Controller* _pids = NULL; + + char * pids_max_val(); public: CgroupV1Subsystem(CgroupV1Controller* cpuset, CgroupV1Controller* cpu, CgroupV1Controller* cpuacct, + CgroupV1Controller* pids, CgroupV1MemoryController* memory) { _cpuset = cpuset; _cpu = new CachingCgroupController(cpu); _cpuacct = cpuacct; + _pids = pids; _memory = new CachingCgroupController(memory); _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Red Hat Inc. + * Copyright (c) 2020, 2022, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ */ int CgroupV2Subsystem::cpu_shares() { GET_CONTAINER_INFO(int, _unified, "/cpu.weight", - "Raw value for CPU shares is: %d", "%d", shares); + "Raw value for CPU Shares is: %d", "%d", shares); // Convert default value of 100 to no shares setup if (shares == 100) { log_debug(os, container)("CPU Shares is: %d", -1); @@ -203,24 +203,6 @@ return limit; } -jlong CgroupV2Subsystem::limit_from_str(char* limit_str) { - if (limit_str == NULL) { - return OSCONTAINER_ERROR; - } - // Unlimited memory in Cgroups V2 is the literal string 'max' - if (strcmp("max", limit_str) == 0) { - os::free(limit_str); - return (jlong)-1; - } - julong limit; - if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) { - os::free(limit_str); - return OSCONTAINER_ERROR; - } - os::free(limit_str); - return (jlong)limit; -} - char* CgroupV2Subsystem::mem_limit_val() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.max", "Raw value for memory limit is: %s", "%s", mem_limit_str, 1024); @@ -244,3 +226,39 @@ return os::strdup(buf); } +char* CgroupV2Subsystem::pids_max_val() { + GET_CONTAINER_INFO_CPTR(cptr, _unified, "/pids.max", + "Maximum number of tasks is: %s", "%s %*d", pidsmax, 1024); + if (pidsmax == NULL) { + return NULL; + } + return os::strdup(pidsmax); +} + +/* pids_max + * + * Return the maximum number of tasks available to the process + * + * return: + * maximum number of tasks + * -1 for unlimited + * OSCONTAINER_ERROR for not supported + */ +jlong CgroupV2Subsystem::pids_max() { + char * pidsmax_str = pids_max_val(); + return limit_from_str(pidsmax_str); +} + +/* pids_current + * + * The number of tasks currently in the cgroup (and its descendants) of the process + * + * return: + * current number of tasks + * OSCONTAINER_ERROR for not supported + */ +jlong CgroupV2Subsystem::pids_current() { + GET_CONTAINER_INFO(jlong, _unified, "/pids.current", + "Current number of tasks is: " JLONG_FORMAT, JLONG_FORMAT, pids_current); + return pids_current; +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -60,7 +60,7 @@ char *mem_swp_limit_val(); char *mem_soft_limit_val(); char *cpu_quota_val(); - jlong limit_from_str(char* limit_str); + char *pids_max_val(); public: CgroupV2Subsystem(CgroupController * unified) { @@ -79,6 +79,9 @@ jlong memory_max_usage_in_bytes(); char * cpu_cpuset_cpus(); char * cpu_cpuset_memory_nodes(); + jlong pids_max(); + jlong pids_current(); + const char * container_type() { return "cgroupv2"; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/osContainer_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/osContainer_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/osContainer_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/osContainer_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -129,3 +129,13 @@ assert(cgroup_subsystem != NULL, "cgroup subsystem not available"); return cgroup_subsystem->cpu_shares(); } + +jlong OSContainer::pids_max() { + assert(cgroup_subsystem != NULL, "cgroup subsystem not available"); + return cgroup_subsystem->pids_max(); +} + +jlong OSContainer::pids_current() { + assert(cgroup_subsystem != NULL, "cgroup subsystem not available"); + return cgroup_subsystem->pids_current(); +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/osContainer_linux.hpp openjdk-17-17.0.3+7/src/hotspot/os/linux/osContainer_linux.hpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/osContainer_linux.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/osContainer_linux.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -62,6 +62,8 @@ static int cpu_shares(); + static jlong pids_max(); + static jlong pids_current(); }; inline bool OSContainer::is_containerized() { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/os_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/os_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/os_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/os_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2233,6 +2233,7 @@ bool os::Linux::print_container_info(outputStream* st) { if (!OSContainer::is_containerized()) { + st->print_cr("container information not found."); return false; } @@ -2321,6 +2322,24 @@ st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "unlimited"); } + j = OSContainer::OSContainer::pids_max(); + st->print("maximum number of tasks: "); + if (j > 0) { + st->print_cr(JLONG_FORMAT, j); + } else { + st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "unlimited"); + } + + j = OSContainer::OSContainer::pids_current(); + st->print("current number of tasks: "); + if (j > 0) { + st->print_cr(JLONG_FORMAT, j); + } else { + if (j == OSCONTAINER_ERROR) { + st->print_cr("not supported"); + } + } + return true; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/posix/signals_posix.cpp openjdk-17-17.0.3+7/src/hotspot/os/posix/signals_posix.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/posix/signals_posix.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/posix/signals_posix.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -562,6 +562,11 @@ { assert(info != NULL && ucVoid != NULL, "sanity"); + if (sig == BREAK_SIGNAL) { + assert(!ReduceSignalUsage, "Should not happen with -Xrs/-XX:+ReduceSignalUsage"); + return true; // ignore it + } + // Note: it's not uncommon that JNI code uses signal/sigset to install, // then restore certain signal handler (e.g. to temporarily block SIGPIPE, // or have a SIGILL handler when detecting CPU type). When that happens, @@ -1197,7 +1202,7 @@ return -1; } -void set_signal_handler(int sig) { +void set_signal_handler(int sig, bool do_check = true) { // Check for overwrite. struct sigaction oldAct; sigaction(sig, (struct sigaction*)NULL, &oldAct); @@ -1241,7 +1246,7 @@ // Save handler setup for later checking vm_handlers.set(sig, &sigAct); - do_check_signal_periodically[sig] = true; + do_check_signal_periodically[sig] = do_check; int ret = sigaction(sig, &sigAct, &oldAct); assert(ret == 0, "check"); @@ -1279,7 +1284,12 @@ set_signal_handler(SIGFPE); PPC64_ONLY(set_signal_handler(SIGTRAP);) set_signal_handler(SIGXFSZ); - + if (!ReduceSignalUsage) { + // This is just for early initialization phase. Intercepting the signal here reduces the risk + // that an attach client accidentally forces HotSpot to quit prematurely. We skip the periodic + // check because late initialization will overwrite it to UserHandler. + set_signal_handler(BREAK_SIGNAL, false); + } #if defined(__APPLE__) // lldb (gdb) installs both standard BSD signal handlers, and mach exception // handlers. By replacing the existing task exception handler, we disable lldb's mach diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/windows/os_windows.cpp openjdk-17-17.0.3+7/src/hotspot/os/windows/os_windows.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/windows/os_windows.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/windows/os_windows.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -681,6 +681,9 @@ return false; } + // Initial state is ALLOCATED but not INITIALIZED + osthread->set_state(ALLOCATED); + // Initialize the JDK library's interrupt event. // This should really be done when OSThread is constructed, // but there is no way for a constructor to report failure to @@ -777,7 +780,7 @@ osthread->set_thread_handle(thread_handle); osthread->set_thread_id(thread_id); - // Initial thread state is INITIALIZED, not SUSPENDED + // Thread state now is INITIALIZED, not SUSPENDED osthread->set_state(INITIALIZED); // The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain diff -Nru openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -382,7 +382,20 @@ extern "C" { int SpinPause() { - return 0; + using spin_wait_func_ptr_t = void (*)(); + spin_wait_func_ptr_t func = CAST_TO_FN_PTR(spin_wait_func_ptr_t, StubRoutines::aarch64::spin_wait()); + assert(func != nullptr, "StubRoutines::aarch64::spin_wait must not be null."); + (*func)(); + // If StubRoutines::aarch64::spin_wait consists of only a RET, + // SpinPause can be considered as implemented. There will be a sequence + // of instructions for: + // - call of SpinPause + // - load of StubRoutines::aarch64::spin_wait stub pointer + // - indirect call of the stub + // - return from the stub + // - return from SpinPause + // So '1' always is returned. + return 1; } void _Copy_conjoint_jshorts_atomic(const jshort* from, jshort* to, size_t count) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2019 SAP SE. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,14 +58,15 @@ // if we were running Java code when SIGPROF came in. if (isInJava) { ucontext_t* uc = (ucontext_t*) ucontext; - frame ret_frame((intptr_t*)uc->uc_mcontext.regs->gpr[1/*REG_SP*/], - (address)uc->uc_mcontext.regs->nip); + address pc = (address)uc->uc_mcontext.regs->nip; - if (ret_frame.pc() == NULL) { + if (pc == NULL) { // ucontext wasn't useful return false; } + frame ret_frame((intptr_t*)uc->uc_mcontext.regs->gpr[1/*REG_SP*/], pc); + if (ret_frame.fp() == NULL) { // The found frame does not have a valid frame pointer. // Bail out because this will create big trouble later on, either diff -Nru openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp --- openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2019 SAP SE. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,14 +58,15 @@ // if we were running Java code when SIGPROF came in. if (isInJava) { ucontext_t* uc = (ucontext_t*) ucontext; - frame ret_frame((intptr_t*)uc->uc_mcontext.gregs[15/*Z_SP*/], - (address)uc->uc_mcontext.psw.addr); + address pc = (address)uc->uc_mcontext.psw.addr; - if (ret_frame.pc() == NULL) { + if (pc == NULL) { // ucontext wasn't useful return false; } + frame ret_frame((intptr_t*)uc->uc_mcontext.gregs[15/*Z_SP*/], pc); + if (ret_frame.fp() == NULL) { // The found frame does not have a valid frame pointer. // Bail out because this will create big trouble later on, either @@ -100,7 +101,7 @@ if (ret_frame.is_interpreted_frame()) { frame::z_ijava_state* istate = ret_frame.ijava_state_unchecked(); - if (is_in_full_stack((address)istate)) { + if (!is_in_full_stack((address)istate)) { return false; } const Method *m = (const Method*)(istate->method); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_IR.cpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_IR.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_IR.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_IR.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -191,7 +191,8 @@ , _oop_map(NULL) , _stack(stack) , _is_method_handle_invoke(false) - , _deoptimize_on_exception(deoptimize_on_exception) { + , _deoptimize_on_exception(deoptimize_on_exception) + , _force_reexecute(false) { assert(_stack != NULL, "must be non null"); } @@ -203,7 +204,8 @@ , _oop_map(NULL) , _stack(stack == NULL ? info->_stack : stack) , _is_method_handle_invoke(info->_is_method_handle_invoke) - , _deoptimize_on_exception(info->_deoptimize_on_exception) { + , _deoptimize_on_exception(info->_deoptimize_on_exception) + , _force_reexecute(info->_force_reexecute) { // deep copy of exception handlers if (info->_exception_handlers != NULL) { @@ -215,7 +217,8 @@ void CodeEmitInfo::record_debug_info(DebugInformationRecorder* recorder, int pc_offset) { // record the safepoint before recording the debug info for enclosing scopes recorder->add_safepoint(pc_offset, _oop_map->deep_copy()); - _scope_debug_info->record_debug_info(recorder, pc_offset, true/*topmost*/, _is_method_handle_invoke); + bool reexecute = _force_reexecute || _scope_debug_info->should_reexecute(); + _scope_debug_info->record_debug_info(recorder, pc_offset, reexecute, _is_method_handle_invoke); recorder->end_safepoint(pc_offset); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_IR.hpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_IR.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_IR.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_IR.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -232,16 +232,15 @@ //Whether we should reexecute this bytecode for deopt bool should_reexecute(); - void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool topmost, bool is_method_handle_invoke = false) { + void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool reexecute, bool is_method_handle_invoke = false) { if (caller() != NULL) { // Order is significant: Must record caller first. - caller()->record_debug_info(recorder, pc_offset, false/*topmost*/); + caller()->record_debug_info(recorder, pc_offset, false/*reexecute*/); } DebugToken* locvals = recorder->create_scope_values(locals()); DebugToken* expvals = recorder->create_scope_values(expressions()); DebugToken* monvals = recorder->create_monitor_values(monitors()); // reexecute allowed only for the topmost frame - bool reexecute = topmost ? should_reexecute() : false; bool return_oop = false; // This flag will be ignored since it used only for C2 with escape analysis. bool rethrow_exception = false; bool is_opt_native = false; @@ -264,6 +263,7 @@ ValueStack* _stack; // used by deoptimization (contains also monitors bool _is_method_handle_invoke; // true if the associated call site is a MethodHandle call site. bool _deoptimize_on_exception; + bool _force_reexecute; // force the reexecute flag on, used for patching stub FrameMap* frame_map() const { return scope()->compilation()->frame_map(); } Compilation* compilation() const { return scope()->compilation(); } @@ -290,7 +290,11 @@ bool is_method_handle_invoke() const { return _is_method_handle_invoke; } void set_is_method_handle_invoke(bool x) { _is_method_handle_invoke = x; } + bool force_reexecute() const { return _force_reexecute; } + void set_force_reexecute() { _force_reexecute = true; } + int interpreter_frame_size() const; + }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_Instruction.cpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_Instruction.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_Instruction.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_Instruction.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -837,6 +837,11 @@ existing_state->invalidate_local(index); TRACE_PHI(tty->print_cr("invalidating local %d because of type mismatch", index)); } + + if (existing_value != new_state->local_at(index) && existing_value->as_Phi() == NULL) { + TRACE_PHI(tty->print_cr("required phi for local %d is missing, irreducible loop?", index)); + return false; // BAILOUT in caller + } } #ifdef ASSERT diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_LIRAssembler.cpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_LIRAssembler.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_LIRAssembler.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_LIRAssembler.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -43,6 +43,7 @@ while ((intx) _masm->pc() - (intx) patch->pc_start() < NativeGeneralJump::instruction_size) { _masm->nop(); } + info->set_force_reexecute(); patch->install(_masm, patch_code, obj, info); append_code_stub(patch); @@ -607,7 +608,7 @@ check_icache(); } offsets()->set_value(CodeOffsets::Verified_Entry, _masm->offset()); - _masm->verified_entry(); + _masm->verified_entry(compilation()->directive()->BreakAtExecuteOption); if (needs_clinit_barrier_on_entry(compilation()->method())) { clinit_barrier(compilation()->method()); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_LIRGenerator.cpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_LIRGenerator.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_LIRGenerator.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_LIRGenerator.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -979,6 +979,14 @@ Phi* phi = sux_val->as_Phi(); // cur_val can be null without phi being null in conjunction with inlining if (phi != NULL && cur_val != NULL && cur_val != phi && !phi->is_illegal()) { + if (phi->is_local()) { + for (int i = 0; i < phi->operand_count(); i++) { + Value op = phi->operand_at(i); + if (op != NULL && op->type()->is_illegal()) { + bailout("illegal phi operand"); + } + } + } Phi* cur_phi = cur_val->as_Phi(); if (cur_phi != NULL && cur_phi->is_illegal()) { // Phi and local would need to get invalidated diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_MacroAssembler.hpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_MacroAssembler.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_MacroAssembler.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_MacroAssembler.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ void build_frame(int frame_size_in_bytes, int bang_size_in_bytes); void remove_frame(int frame_size_in_bytes); - void verified_entry(); + void verified_entry(bool breakAtEntry); void verify_stack_oop(int offset) PRODUCT_RETURN; void verify_not_null_oop(Register r) PRODUCT_RETURN; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/cds/dynamicArchive.cpp openjdk-17-17.0.3+7/src/hotspot/share/cds/dynamicArchive.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/cds/dynamicArchive.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/cds/dynamicArchive.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -111,6 +111,7 @@ // Block concurrent class unloading from changing the _dumptime_table MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); SystemDictionaryShared::check_excluded_classes(); + SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary(); init_header(); gather_source_objs(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/cds/metaspaceShared.cpp openjdk-17-17.0.3+7/src/hotspot/share/cds/metaspaceShared.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/cds/metaspaceShared.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/cds/metaspaceShared.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -487,6 +487,7 @@ // Block concurrent class unloading from changing the _dumptime_table MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); SystemDictionaryShared::check_excluded_classes(); + SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary(); StaticArchiveBuilder builder; builder.gather_source_objs(); @@ -550,20 +551,19 @@ class CollectCLDClosure : public CLDClosure { GrowableArray _loaded_cld; - GrowableArray _loaded_cld_handles; // keep the CLDs alive + GrowableArray _loaded_cld_handles; // keep the CLDs alive Thread* _current_thread; public: CollectCLDClosure(Thread* thread) : _current_thread(thread) {} ~CollectCLDClosure() { - for (int i = 0; i < _loaded_cld.length(); i++) { - ClassLoaderData* cld = _loaded_cld.at(i); + for (int i = 0; i < _loaded_cld_handles.length(); i++) { + _loaded_cld_handles.at(i).release(Universe::vm_global()); } } void do_cld(ClassLoaderData* cld) { - if (!cld->is_unloading()) { - _loaded_cld.append(cld); - _loaded_cld_handles.append(Handle(_current_thread, cld->holder_phantom())); - } + assert(cld->is_alive(), "must be"); + _loaded_cld.append(cld); + _loaded_cld_handles.append(OopHandle(Universe::vm_global(), cld->holder_phantom())); } int nof_cld() const { return _loaded_cld.length(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/altHashing.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/altHashing.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/altHashing.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/altHashing.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -26,18 +26,23 @@ * halfsiphash code adapted from reference implementation * (https://github.com/veorq/SipHash/blob/master/halfsiphash.c) * which is distributed with the following copyright: - * - * SipHash reference C implementation - * - * Copyright (c) 2016 Jean-Philippe Aumasson - * - * To the extent possible under law, the author(s) have dedicated all copyright - * and related and neighboring rights to this software to the public domain - * worldwide. This software is distributed without any warranty. - * - * You should have received a copy of the CC0 Public Domain Dedication along - * with this software. If not, see - * . + */ + +/* + SipHash reference C implementation + + Copyright (c) 2012-2021 Jean-Philippe Aumasson + + Copyright (c) 2012-2014 Daniel J. Bernstein + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along + with + this software. If not, see + . */ #include "precompiled.hpp" @@ -135,7 +140,9 @@ } // HalfSipHash-2-4 (32-bit output) for Symbols -uint32_t AltHashing::halfsiphash_32(uint64_t seed, const uint8_t* data, int len) { +uint32_t AltHashing::halfsiphash_32(uint64_t seed, const void* in, int len) { + + const unsigned char* data = (const unsigned char*)in; uint32_t v[4]; uint32_t newdata; int off = 0; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/altHashing.hpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/altHashing.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/altHashing.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/altHashing.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ static uint64_t compute_seed(); // For Symbols - static uint32_t halfsiphash_32(uint64_t seed, const uint8_t* data, int len); + static uint32_t halfsiphash_32(uint64_t seed, const void* in, int len); // For Strings static uint32_t halfsiphash_32(uint64_t seed, const uint16_t* data, int len); }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/classLoaderData.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/classLoaderData.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/classLoaderData.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/classLoaderData.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -55,6 +55,7 @@ #include "classfile/packageEntry.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" @@ -886,6 +887,10 @@ // Remove the class so unloading events aren't triggered for // this class (scratch or error class) in do_unloading(). remove_class(ik); + // But still have to remove it from the dumptime_table. + if (Arguments::is_dumping_archive()) { + SystemDictionaryShared::remove_dumptime_info(ik); + } } } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/symbolTable.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/symbolTable.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/symbolTable.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/symbolTable.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -91,7 +91,14 @@ static volatile bool _alt_hash = false; + +#ifdef USE_LIBRARY_BASED_TLS_ONLY static volatile bool _lookup_shared_first = false; +#else +// "_lookup_shared_first" can get highly contended with many cores if multiple threads +// are updating "lookup success history" in a global shared variable. If built-in TLS is available, use it. +static THREAD_LOCAL bool _lookup_shared_first = false; +#endif // Static arena for symbols that are not deallocated Arena* SymbolTable::_arena = NULL; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/systemDictionaryShared.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/systemDictionaryShared.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/systemDictionaryShared.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/systemDictionaryShared.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2405,6 +2405,28 @@ return false; } +class CleanupDumpTimeLambdaProxyClassTable: StackObj { + public: + bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { + assert_lock_strong(DumpTimeTable_lock); + for (int i = 0; i < info._proxy_klasses->length(); i++) { + InstanceKlass* ik = info._proxy_klasses->at(i); + if (!ik->can_be_verified_at_dumptime()) { + info._proxy_klasses->remove_at(i); + } + } + return info._proxy_klasses->length() == 0 ? true /* delete the node*/ : false; + } +}; + +void SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary() { + assert_lock_strong(DumpTimeTable_lock); + if (_dumptime_lambda_proxy_class_dictionary != NULL) { + CleanupDumpTimeLambdaProxyClassTable cleanup_proxy_classes; + _dumptime_lambda_proxy_class_dictionary->unlink(&cleanup_proxy_classes); + } +} + #if INCLUDE_CDS_JAVA_HEAP class ArchivedMirrorPatcher { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/systemDictionaryShared.hpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/systemDictionaryShared.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/systemDictionaryShared.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/systemDictionaryShared.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -331,6 +331,7 @@ static size_t estimate_size_for_archive(); static void write_to_archive(bool is_static_archive = true); static void adjust_lambda_proxy_class_dictionary(); + static void cleanup_lambda_proxy_class_dictionary(); static void serialize_dictionary_headers(class SerializeClosure* soc, bool is_static_archive = true); static void serialize_vm_classes(class SerializeClosure* soc); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/verifier.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/verifier.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/verifier.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/verifier.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1805,7 +1805,7 @@ no_control_flow = true; break; default: // We only need to check the valid bytecodes in class file. - // And jsr and ret are not in the new class file format in JDK1.5. + // And jsr and ret are not in the new class file format in JDK1.6. verify_error(ErrorContext::bad_code(bci), "Bad instruction: %02x", opcode); no_control_flow = false; @@ -2424,7 +2424,8 @@ verify_error(ErrorContext::bad_type(bci, current_frame->stack_top_ctx(), TypeOrigin::implicit(current_type())), - "Bad access to protected data in getfield"); + "Bad access to protected data in %s", + is_getfield ? "getfield" : "putfield"); return; } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/code/codeCache.cpp openjdk-17-17.0.3+7/src/hotspot/share/code/codeCache.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/code/codeCache.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/code/codeCache.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -678,7 +678,7 @@ void CodeCache::metadata_do(MetadataClosure* f) { assert_locked_or_safepoint(CodeCache_lock); - NMethodIterator iter(NMethodIterator::only_alive_and_not_unloading); + NMethodIterator iter(NMethodIterator::only_alive); while(iter.next()) { iter.method()->metadata_do(f); } @@ -1032,7 +1032,7 @@ } #if INCLUDE_JVMTI -// RedefineClasses support for unloading nmethods that are dependent on "old" methods. +// RedefineClasses support for saving nmethods that are dependent on "old" methods. // We don't really expect this table to grow very large. If it does, it can become a hashtable. static GrowableArray* old_compiled_method_table = NULL; @@ -1091,7 +1091,7 @@ reset_old_method_table(); int number_of_marked_CodeBlobs = 0; - CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + CompiledMethodIterator iter(CompiledMethodIterator::only_alive); while(iter.next()) { CompiledMethod* nm = iter.method(); // Walk all alive nmethods to check for old Methods. @@ -1111,7 +1111,7 @@ void CodeCache::mark_all_nmethods_for_evol_deoptimization() { assert(SafepointSynchronize::is_at_safepoint(), "Can only do this at a safepoint!"); - CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + CompiledMethodIterator iter(CompiledMethodIterator::only_alive); while(iter.next()) { CompiledMethod* nm = iter.method(); if (!nm->method()->is_method_handle_intrinsic()) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/compiler/compileBroker.cpp openjdk-17-17.0.3+7/src/hotspot/share/compiler/compileBroker.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/compiler/compileBroker.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/compiler/compileBroker.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -410,6 +410,7 @@ CompileTask::free(current); } _first = NULL; + _last = NULL; // Wake up all threads that block on the queue. MethodCompileQueue_lock->notify_all(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/compiler/compilerDirectives.cpp openjdk-17-17.0.3+7/src/hotspot/share/compiler/compilerDirectives.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/compiler/compilerDirectives.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/compiler/compilerDirectives.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -334,9 +334,21 @@ if (!CompilerDirectivesIgnoreCompileCommandsOption && CompilerOracle::has_any_command_set()) { DirectiveSetPtr set(this); +#ifdef COMPILER1 + if (C1Breakpoint) { + // If the directives didn't have 'BreakAtExecute', + // the command 'C1Breakpoint' would become effective. + if (!_modified[BreakAtExecuteIndex]) { + set.cloned()->BreakAtExecuteOption = true; + } + } +#endif + // All CompileCommands are not equal so this gets a bit verbose // When CompileCommands have been refactored less clutter will remain. if (CompilerOracle::should_break_at(method)) { + // If the directives didn't have 'BreakAtCompile' or 'BreakAtExecute', + // the sub-command 'Break' of the 'CompileCommand' would become effective. if (!_modified[BreakAtCompileIndex]) { set.cloned()->BreakAtCompileOption = true; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/g1/g1RemSet.cpp openjdk-17-17.0.3+7/src/hotspot/share/gc/g1/g1RemSet.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/g1/g1RemSet.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/g1/g1RemSet.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -107,7 +107,7 @@ // within a region to claim. Dependent on the region size as proxy for the heap // size, we limit the total number of chunks to limit memory usage and maintenance // effort of that table vs. granularity of distributing scanning work. - // Testing showed that 8 for 1M/2M region, 16 for 4M/8M regions, 32 for 16/32M regions + // Testing showed that 64 for 1M/2M region, 128 for 4M/8M regions, 256 for 16/32M regions // seems to be such a good trade-off. static uint get_chunks_per_region(uint log_region_size) { // Limit the expected input values to current known possible values of the @@ -115,7 +115,7 @@ // values for region size. assert(log_region_size >= 20 && log_region_size <= 25, "expected value in [20,25], but got %u", log_region_size); - return 1u << (log_region_size / 2 - 7); + return 1u << (log_region_size / 2 - 4); } uint _scan_chunks_per_region; // Number of chunks per region. diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -29,10 +29,10 @@ #include "runtime/semaphore.hpp" #include "runtime/thread.inline.hpp" -uint SuspendibleThreadSet::_nthreads = 0; -uint SuspendibleThreadSet::_nthreads_stopped = 0; -bool SuspendibleThreadSet::_suspend_all = false; -double SuspendibleThreadSet::_suspend_all_start = 0.0; +uint SuspendibleThreadSet::_nthreads = 0; +uint SuspendibleThreadSet::_nthreads_stopped = 0; +volatile bool SuspendibleThreadSet::_suspend_all = false; +double SuspendibleThreadSet::_suspend_all_start = 0.0; static Semaphore* _synchronize_wakeup = NULL; @@ -50,7 +50,7 @@ void SuspendibleThreadSet::join() { assert(!Thread::current()->is_suspendible_thread(), "Thread already joined"); MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - while (_suspend_all) { + while (suspend_all()) { ml.wait(); } _nthreads++; @@ -63,7 +63,7 @@ assert(_nthreads > 0, "Invalid"); DEBUG_ONLY(Thread::current()->clear_suspendible_thread();) _nthreads--; - if (_suspend_all && is_synchronized()) { + if (suspend_all() && is_synchronized()) { // This leave completes a request, so inform the requestor. _synchronize_wakeup->signal(); } @@ -72,7 +72,7 @@ void SuspendibleThreadSet::yield() { assert(Thread::current()->is_suspendible_thread(), "Must have joined"); MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - if (_suspend_all) { + if (suspend_all()) { _nthreads_stopped++; if (is_synchronized()) { if (ConcGCYieldTimeout > 0) { @@ -82,7 +82,7 @@ // This yield completes the request, so inform the requestor. _synchronize_wakeup->signal(); } - while (_suspend_all) { + while (suspend_all()) { ml.wait(); } assert(_nthreads_stopped > 0, "Invalid"); @@ -97,8 +97,8 @@ } { MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - assert(!_suspend_all, "Only one at a time"); - _suspend_all = true; + assert(!suspend_all(), "Only one at a time"); + Atomic::store(&_suspend_all, true); if (is_synchronized()) { return; } @@ -120,7 +120,7 @@ #ifdef ASSERT MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - assert(_suspend_all, "STS not synchronizing"); + assert(suspend_all(), "STS not synchronizing"); assert(is_synchronized(), "STS not synchronized"); #endif } @@ -128,8 +128,8 @@ void SuspendibleThreadSet::desynchronize() { assert(Thread::current()->is_VM_thread(), "Must be the VM thread"); MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - assert(_suspend_all, "STS not synchronizing"); + assert(suspend_all(), "STS not synchronizing"); assert(is_synchronized(), "STS not synchronized"); - _suspend_all = false; + Atomic::store(&_suspend_all, false); ml.notify_all(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -26,6 +26,7 @@ #define SHARE_GC_SHARED_SUSPENDIBLETHREADSET_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" // A SuspendibleThreadSet is a set of threads that can be suspended. // A thread can join and later leave the set, and periodically yield. @@ -40,9 +41,10 @@ friend class SuspendibleThreadSetLeaver; private: + static volatile bool _suspend_all; + static uint _nthreads; static uint _nthreads_stopped; - static bool _suspend_all; static double _suspend_all_start; static bool is_synchronized(); @@ -53,9 +55,11 @@ // Removes the current thread from the set. static void leave(); + static bool suspend_all() { return Atomic::load(&_suspend_all); } + public: // Returns true if an suspension is in progress. - static bool should_yield() { return _suspend_all; } + static bool should_yield() { return suspend_all(); } // Suspends the current thread if a suspension is in progress. static void yield(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -227,7 +227,7 @@ void ShenandoahCodeBlobAndDisarmClosure::do_code_blob(CodeBlob* cb) { nmethod* const nm = cb->as_nmethod_or_null(); - if (nm != NULL && nm->oops_do_try_claim()) { + if (nm != NULL) { assert(!ShenandoahNMethod::gc_data(nm)->is_unregistered(), "Should not be here"); CodeBlobToOopClosure::do_code_blob(cb); _bs->disarm(nm); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -261,7 +261,9 @@ if ((STRING_DEDUP == ENQUEUE_DEDUP) && ShenandoahStringDedup::is_candidate(obj)) { assert(ShenandoahStringDedup::is_enabled(), "Must be enabled"); req->add(obj); - } else if ((STRING_DEDUP == ALWAYS_DEDUP) && ShenandoahStringDedup::is_string_candidate(obj)) { + } else if ((STRING_DEDUP == ALWAYS_DEDUP) && + ShenandoahStringDedup::is_string_candidate(obj) && + !ShenandoahStringDedup::dedup_requested(obj)) { assert(ShenandoahStringDedup::is_enabled(), "Must be enabled"); req->add(obj); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -79,7 +79,6 @@ } ShenandoahCodeCacheRoots::ShenandoahCodeCacheRoots(ShenandoahPhaseTimings::Phase phase) : _phase(phase) { - nmethod::oops_do_marking_prologue(); } void ShenandoahCodeCacheRoots::code_blobs_do(CodeBlobClosure* blob_cl, uint worker_id) { @@ -87,10 +86,6 @@ _coderoots_iterator.possibly_parallel_blobs_do(blob_cl); } -ShenandoahCodeCacheRoots::~ShenandoahCodeCacheRoots() { - nmethod::oops_do_marking_epilogue(); -} - ShenandoahRootProcessor::ShenandoahRootProcessor(ShenandoahPhaseTimings::Phase phase) : _heap(ShenandoahHeap::heap()), _phase(phase), @@ -258,22 +253,44 @@ _code_roots(ShenandoahPhaseTimings::heap_iteration_roots) { } - void ShenandoahHeapIterationRootScanner::roots_do(OopClosure* oops) { - assert(Thread::current()->is_VM_thread(), "Only by VM thread"); - // Must use _claim_none to avoid interfering with concurrent CLDG iteration - CLDToOopClosure clds(oops, ClassLoaderData::_claim_none); - MarkingCodeBlobClosure code(oops, !CodeBlobToOopClosure::FixRelocations); - ShenandoahParallelOopsDoThreadClosure tc_cl(oops, &code, NULL); - AlwaysTrueClosure always_true; - - ResourceMark rm; - - // Process light-weight/limited parallel roots then - _vm_roots.oops_do(oops, 0); - _weak_roots.oops_do(oops, 0); - _cld_roots.cld_do(&clds, 0); - - // Process heavy-weight/fully parallel roots the last - _code_roots.code_blobs_do(&code, 0); - _thread_roots.threads_do(&tc_cl, 0); - } +class ShenandoahMarkCodeBlobClosure : public CodeBlobClosure { +private: + OopClosure* const _oops; + BarrierSetNMethod* const _bs_nm; + +public: + ShenandoahMarkCodeBlobClosure(OopClosure* oops) : + _oops(oops), + _bs_nm(BarrierSet::barrier_set()->barrier_set_nmethod()) {} + + virtual void do_code_blob(CodeBlob* cb) { + nmethod* const nm = cb->as_nmethod_or_null(); + if (nm != nullptr) { + if (_bs_nm != nullptr) { + // Make sure it only sees to-space objects + _bs_nm->nmethod_entry_barrier(nm); + } + ShenandoahNMethod* const snm = ShenandoahNMethod::gc_data(nm); + assert(snm != nullptr, "Sanity"); + snm->oops_do(_oops, false /*fix_relocations*/); + } + } +}; + +void ShenandoahHeapIterationRootScanner::roots_do(OopClosure* oops) { + // Must use _claim_none to avoid interfering with concurrent CLDG iteration + CLDToOopClosure clds(oops, ClassLoaderData::_claim_none); + ShenandoahMarkCodeBlobClosure code(oops); + ShenandoahParallelOopsDoThreadClosure tc_cl(oops, &code, NULL); + + ResourceMark rm; + + // Process light-weight/limited parallel roots then + _vm_roots.oops_do(oops, 0); + _weak_roots.oops_do(oops, 0); + _cld_roots.cld_do(&clds, 0); + + // Process heavy-weight/fully parallel roots the last + _code_roots.code_blobs_do(&code, 0); + _thread_roots.threads_do(&tc_cl, 0); +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -102,7 +102,6 @@ ShenandoahCodeRootsIterator _coderoots_iterator; public: ShenandoahCodeCacheRoots(ShenandoahPhaseTimings::Phase phase); - ~ShenandoahCodeCacheRoots(); void code_blobs_do(CodeBlobClosure* blob_cl, uint worker_id); }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -31,6 +31,7 @@ public: static inline bool is_string_candidate(oop obj); static inline bool is_candidate(oop obj); + static inline bool dedup_requested(oop obj); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_HPP diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -36,6 +36,10 @@ java_lang_String::value(obj) != nullptr; } +bool ShenandoahStringDedup::dedup_requested(oop obj) { + return java_lang_String::test_and_set_deduplication_requested(obj); +} + bool ShenandoahStringDedup::is_candidate(oop obj) { if (!is_string_candidate(obj)) { return false; @@ -51,7 +55,8 @@ // Increase string age and enqueue it when it rearches age threshold markWord new_mark = mark.incr_age(); if (mark == obj->cas_set_mark(new_mark, mark)) { - return StringDedup::is_threshold_age(new_mark.age()); + return StringDedup::is_threshold_age(new_mark.age()) && + !dedup_requested(obj); } } return false; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/interpreter/bytecodeUtils.cpp openjdk-17-17.0.3+7/src/hotspot/share/interpreter/bytecodeUtils.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/interpreter/bytecodeUtils.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/interpreter/bytecodeUtils.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1029,7 +1029,6 @@ break; case Bytecodes::_arraylength: - // The return type of arraylength is wrong in the bytecodes table (T_VOID). stack->pop(1); stack->push(bci, T_INT); break; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/interpreter/bytecodes.cpp openjdk-17-17.0.3+7/src/hotspot/share/interpreter/bytecodes.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/interpreter/bytecodes.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/interpreter/bytecodes.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -471,7 +471,7 @@ def(_new , "new" , "bkk" , NULL , T_OBJECT , 1, true ); def(_newarray , "newarray" , "bc" , NULL , T_OBJECT , 0, true ); def(_anewarray , "anewarray" , "bkk" , NULL , T_OBJECT , 0, true ); - def(_arraylength , "arraylength" , "b" , NULL , T_VOID , 0, true ); + def(_arraylength , "arraylength" , "b" , NULL , T_INT , 0, true ); def(_athrow , "athrow" , "b" , NULL , T_VOID , -1, true ); def(_checkcast , "checkcast" , "bkk" , NULL , T_OBJECT , 0, true ); def(_instanceof , "instanceof" , "bkk" , NULL , T_INT , 0, true ); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp openjdk-17-17.0.3+7/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -403,25 +403,25 @@ template void BytecodeInterpreter::run(interpreterState istate) { - - // In order to simplify some tests based on switches set at runtime - // we invoke the interpreter a single time after switches are enabled - // and set simpler to to test variables rather than method calls or complex - // boolean expressions. - - static int initialized = 0; - static int checkit = 0; - static intptr_t* c_addr = NULL; - static intptr_t c_value; - - if (checkit && *c_addr != c_value) { - os::breakpoint(); - } + intptr_t* topOfStack = (intptr_t *)istate->stack(); /* access with STACK macros */ + address pc = istate->bcp(); + jubyte opcode; + intptr_t* locals = istate->locals(); + ConstantPoolCache* cp = istate->constants(); // method()->constants()->cache() +#ifdef LOTS_OF_REGS + JavaThread* THREAD = istate->thread(); +#else +#undef THREAD +#define THREAD istate->thread() +#endif #ifdef ASSERT - if (istate->_msg != initialize) { - assert(labs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + 1), "bad stack limit"); - } + assert(labs(istate->stack_base() - istate->stack_limit()) == (istate->method()->max_stack() + 1), + "Bad stack limit"); + /* QQQ this should be a stack method so we don't know actual direction */ + assert(topOfStack >= istate->stack_limit() && topOfStack < istate->stack_base(), + "Stack top out of range"); + // Verify linkages. interpreterState l = istate; do { @@ -433,18 +433,6 @@ interpreterState orig = istate; #endif - intptr_t* topOfStack = (intptr_t *)istate->stack(); /* access with STACK macros */ - address pc = istate->bcp(); - jubyte opcode; - intptr_t* locals = istate->locals(); - ConstantPoolCache* cp = istate->constants(); // method()->constants()->cache() -#ifdef LOTS_OF_REGS - JavaThread* THREAD = istate->thread(); -#else -#undef THREAD -#define THREAD istate->thread() -#endif - #ifdef USELABELS const static void* const opclabels_data[256] = { /* 0x00 */ &&opc_nop, &&opc_aconst_null, &&opc_iconst_m1, &&opc_iconst_0, @@ -531,38 +519,13 @@ uintptr_t *dispatch_table = (uintptr_t*)&opclabels_data[0]; #endif /* USELABELS */ -#ifdef ASSERT - // this will trigger a VERIFY_OOP on entry - if (istate->msg() != initialize && ! METHOD->is_static()) { - oop rcvr = LOCALS_OBJECT(0); - VERIFY_OOP(rcvr); - } -#endif - - /* QQQ this should be a stack method so we don't know actual direction */ - guarantee(istate->msg() == initialize || - topOfStack >= istate->stack_limit() && - topOfStack < istate->stack_base(), - "Stack top out of range"); - - assert(!UseCompiler, "Zero does not support compilers"); - assert(!CountCompiledCalls, "Zero does not support counting compiled calls"); - switch (istate->msg()) { case initialize: { - if (initialized++) ShouldNotReachHere(); // Only one initialize call. + ShouldNotCallThis(); return; } - break; case method_entry: { THREAD->set_do_not_unlock(); - // count invocations - assert(initialized, "Interpreter not initialized"); - - if ((istate->_stack_base - istate->_stack_limit) != istate->method()->max_stack() + 1) { - // initialize - os::breakpoint(); - } // Lock method if synchronized. if (METHOD->is_synchronized()) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -243,6 +243,7 @@ int write__klass__leakp(JfrCheckpointWriter* writer, const void* k) { assert(k != NULL, "invariant"); KlassPtr klass = (KlassPtr)k; + CLEAR_LEAKP(klass); return write_klass(writer, klass, true); } @@ -848,7 +849,7 @@ private: MethodCallback _method_cb; KlassCallback _klass_cb; - MethodUsedPredicate _method_used_predicate; + MethodUsedPredicate _method_used_predicate; MethodFlagPredicate _method_flag_predicate; public: MethodIteratorHost(JfrCheckpointWriter* writer, diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -146,16 +146,12 @@ } }; -template class MethodUsedPredicate { bool _current_epoch; public: MethodUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {} bool operator()(const Klass* klass) { - if (_current_epoch) { - return leakp ? IS_LEAKP(klass) : METHOD_USED_THIS_EPOCH(klass); - } - return leakp ? IS_LEAKP(klass) : METHOD_USED_PREVIOUS_EPOCH(klass); + return _current_epoch ? METHOD_USED_THIS_EPOCH(klass) : METHOD_USED_PREVIOUS_EPOCH(klass); } }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -207,7 +207,7 @@ const u4 checkpoint_size = current_offset() - event_size_offset; write_padded_at_offset(checkpoint_size, event_size_offset); set_last_checkpoint_offset(event_size_offset); - const size_t sz_written = size_written(); + const int64_t sz_written = size_written(); write_be_at_offset(sz_written, chunk_size_offset); return sz_written; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp openjdk-17-17.0.3+7/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1832,7 +1832,7 @@ return JVMCIENV->get_jobjectArray(methods); C2V_END -C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, jobject expected_type, long displacement, jboolean is_volatile, jobject kind_object)) +C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, jobject expected_type, long displacement, jobject kind_object)) if (object == NULL || kind_object == NULL) { JVMCI_THROW_0(NullPointerException); } @@ -1872,13 +1872,18 @@ ShouldNotReachHere(); } - if (displacement < 0 || ((long) displacement + type2aelembytes(basic_type) > HeapWordSize * obj->size())) { + int basic_type_elemsize = type2aelembytes(basic_type); + if (displacement < 0 || ((long) displacement + basic_type_elemsize > HeapWordSize * obj->size())) { // Reading outside of the object bounds JVMCI_THROW_MSG_NULL(IllegalArgumentException, "reading outside object bounds"); } // Perform basic sanity checks on the read. Primitive reads are permitted to read outside the // bounds of their fields but object reads must map exactly onto the underlying oop slot. + bool aligned = (displacement % basic_type_elemsize) == 0; + if (!aligned) { + JVMCI_THROW_MSG_NULL(IllegalArgumentException, "read is unaligned"); + } if (basic_type == T_OBJECT) { if (obj->is_objArray()) { if (displacement < arrayOopDesc::base_offset_in_bytes(T_OBJECT)) { @@ -1916,15 +1921,20 @@ } jlong value = 0; + + // Treat all reads as volatile for simplicity as this function can be used + // both for reading Java fields declared as volatile as well as for constant + // folding Unsafe.get* methods with volatile semantics. + switch (basic_type) { - case T_BOOLEAN: value = is_volatile ? obj->bool_field_acquire(displacement) : obj->bool_field(displacement); break; - case T_BYTE: value = is_volatile ? obj->byte_field_acquire(displacement) : obj->byte_field(displacement); break; - case T_SHORT: value = is_volatile ? obj->short_field_acquire(displacement) : obj->short_field(displacement); break; - case T_CHAR: value = is_volatile ? obj->char_field_acquire(displacement) : obj->char_field(displacement); break; + case T_BOOLEAN: value = obj->bool_field_acquire(displacement); break; + case T_BYTE: value = obj->byte_field_acquire(displacement); break; + case T_SHORT: value = obj->short_field_acquire(displacement); break; + case T_CHAR: value = obj->char_field_acquire(displacement); break; case T_FLOAT: - case T_INT: value = is_volatile ? obj->int_field_acquire(displacement) : obj->int_field(displacement); break; + case T_INT: value = obj->int_field_acquire(displacement); break; case T_DOUBLE: - case T_LONG: value = is_volatile ? obj->long_field_acquire(displacement) : obj->long_field(displacement); break; + case T_LONG: value = obj->long_field_acquire(displacement); break; case T_OBJECT: { if (displacement == java_lang_Class::component_mirror_offset() && java_lang_Class::is_instance(obj()) && @@ -1934,7 +1944,8 @@ return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_NULL_POINTER()); } - oop value = is_volatile ? obj->obj_field_acquire(displacement) : obj->obj_field(displacement); + oop value = obj->obj_field_acquire(displacement); + if (value == NULL) { return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_NULL_POINTER()); } else { @@ -2680,8 +2691,8 @@ {CC "boxPrimitive", CC "(" OBJECT ")" OBJECTCONSTANT, FN_PTR(boxPrimitive)}, {CC "getDeclaredConstructors", CC "(" HS_RESOLVED_KLASS ")[" RESOLVED_METHOD, FN_PTR(getDeclaredConstructors)}, {CC "getDeclaredMethods", CC "(" HS_RESOLVED_KLASS ")[" RESOLVED_METHOD, FN_PTR(getDeclaredMethods)}, - {CC "readFieldValue", CC "(" HS_RESOLVED_KLASS HS_RESOLVED_KLASS "JZLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, - {CC "readFieldValue", CC "(" OBJECTCONSTANT HS_RESOLVED_KLASS "JZLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, + {CC "readFieldValue", CC "(" HS_RESOLVED_KLASS HS_RESOLVED_KLASS "JLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, + {CC "readFieldValue", CC "(" OBJECTCONSTANT HS_RESOLVED_KLASS "JLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, {CC "isInstance", CC "(" HS_RESOLVED_KLASS OBJECTCONSTANT ")Z", FN_PTR(isInstance)}, {CC "isAssignableFrom", CC "(" HS_RESOLVED_KLASS HS_RESOLVED_KLASS ")Z", FN_PTR(isAssignableFrom)}, {CC "isTrustedForIntrinsics", CC "(" HS_RESOLVED_KLASS ")Z", FN_PTR(isTrustedForIntrinsics)}, diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jvmci/vmStructs_jvmci.cpp openjdk-17-17.0.3+7/src/hotspot/share/jvmci/vmStructs_jvmci.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/jvmci/vmStructs_jvmci.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jvmci/vmStructs_jvmci.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -565,8 +565,8 @@ declare_constant(Deoptimization::Reason_not_compiled_exception_handler) \ declare_constant(Deoptimization::Reason_unresolved) \ declare_constant(Deoptimization::Reason_jsr_mismatch) \ - declare_constant(Deoptimization::Reason_LIMIT) \ - declare_constant(Deoptimization::_support_large_access_byte_array_virtualization) \ + declare_constant(Deoptimization::Reason_TRAP_HISTORY_LENGTH) \ + declare_constant(Deoptimization::_support_large_access_byte_array_virtualization) \ \ declare_constant(FieldInfo::access_flags_offset) \ declare_constant(FieldInfo::name_index_offset) \ diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/allocation.hpp openjdk-17-17.0.3+7/src/hotspot/share/memory/allocation.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/allocation.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/allocation.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -145,6 +145,7 @@ f(mtServiceability, "Serviceability") \ f(mtMetaspace, "Metaspace") \ f(mtStringDedup, "String Deduplication") \ + f(mtObjectMonitor, "Object Monitors") \ f(mtNone, "Unknown") \ //end diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -42,6 +42,7 @@ _by_spacetype("by-spacetype", "Break down numbers by loader type.", "BOOLEAN", false, "false"), _by_chunktype("by-chunktype", "Break down numbers by chunk type.", "BOOLEAN", false, "false"), _show_vslist("vslist", "Shows details about the underlying virtual space.", "BOOLEAN", false, "false"), + _show_chunkfreelist("chunkfreelist", "Shows details about global chunk free lists (ChunkManager).", "BOOLEAN", false, "false"), _scale("scale", "Memory usage in which to scale. Valid values are: 1, KB, MB or GB (fixed scale) " "or \"dynamic\" for a dynamically choosen scale.", "STRING", false, "dynamic"), @@ -53,6 +54,7 @@ _dcmdparser.add_dcmd_option(&_by_chunktype); _dcmdparser.add_dcmd_option(&_by_spacetype); _dcmdparser.add_dcmd_option(&_show_vslist); + _dcmdparser.add_dcmd_option(&_show_chunkfreelist); _dcmdparser.add_dcmd_option(&_scale); } @@ -96,6 +98,7 @@ if (_by_chunktype.value()) flags |= (int)MetaspaceReporter::Option::BreakDownByChunkType; if (_by_spacetype.value()) flags |= (int)MetaspaceReporter::Option::BreakDownBySpaceType; if (_show_vslist.value()) flags |= (int)MetaspaceReporter::Option::ShowVSList; + if (_show_chunkfreelist.value()) flags |= (int)MetaspaceReporter::Option::ShowChunkFreeList; VM_PrintMetadata op(output(), scale, flags); VMThread::execute(&op); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -38,6 +38,7 @@ DCmdArgument _by_spacetype; DCmdArgument _by_chunktype; DCmdArgument _show_vslist; + DCmdArgument _show_chunkfreelist; DCmdArgument _scale; DCmdArgument _show_classes; public: diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -300,6 +300,24 @@ out->cr(); } + // -- Print Chunkmanager details. + if ((flags & (int)Option::ShowChunkFreeList) > 0) { + out->cr(); + out->print_cr("Chunk freelist details:"); + if (Metaspace::using_class_space()) { + out->print_cr(" Non-Class:"); + } + ChunkManager::chunkmanager_nonclass()->print_on(out); + out->cr(); + if (Metaspace::using_class_space()) { + out->print_cr(" Class:"); + ChunkManager::chunkmanager_class()->print_on(out); + out->cr(); + } + } + out->cr(); + + //////////// Waste section /////////////////////////// // As a convenience, print a summary of common waste. out->cr(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -44,7 +44,9 @@ // Print details about the underlying virtual spaces. ShowVSList = (1 << 3), // If show_loaders: show loaded classes for each loader. - ShowClasses = (1 << 4) + ShowClasses = (1 << 4), + // Print details about the underlying virtual spaces. + ShowChunkFreeList = (1 << 5) }; // This will print out a basic metaspace usage report but diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/instanceKlass.cpp openjdk-17-17.0.3+7/src/hotspot/share/oops/instanceKlass.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/instanceKlass.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/instanceKlass.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -589,7 +589,10 @@ // Release C heap allocated data that this points to, which includes // reference counting symbol names. - release_C_heap_structures_internal(); + // Can't release the constant pool here because the constant pool can be + // deallocated separately from the InstanceKlass for default methods and + // redefine classes. + release_C_heap_structures(/* release_constant_pool */ false); deallocate_methods(loader_data, methods()); set_methods(NULL); @@ -1593,7 +1596,8 @@ void InstanceKlass::methods_do(void f(Method* method)) { // Methods aren't stable until they are loaded. This can be read outside // a lock through the ClassLoaderData for profiling - if (!is_loaded()) { + // Redefined scratch classes are on the list and need to be cleaned + if (!is_loaded() && !is_scratch_class()) { return; } @@ -2009,29 +2013,18 @@ return NULL; } -/* jni_id_for_impl for jfieldIds only */ -JNIid* InstanceKlass::jni_id_for_impl(int offset) { +/* jni_id_for for jfieldIds only */ +JNIid* InstanceKlass::jni_id_for(int offset) { MutexLocker ml(JfieldIdCreation_lock); - // Retry lookup after we got the lock JNIid* probe = jni_ids() == NULL ? NULL : jni_ids()->find(offset); if (probe == NULL) { - // Slow case, allocate new static field identifier + // Allocate new static field identifier probe = new JNIid(this, offset, jni_ids()); set_jni_ids(probe); } return probe; } - -/* jni_id_for for jfieldIds only */ -JNIid* InstanceKlass::jni_id_for(int offset) { - JNIid* probe = jni_ids() == NULL ? NULL : jni_ids()->find(offset); - if (probe == NULL) { - probe = jni_id_for_impl(offset); - } - return probe; -} - u2 InstanceKlass::enclosing_method_data(int offset) const { const Array* const inner_class_list = inner_classes(); if (inner_class_list == NULL) { @@ -2641,22 +2634,13 @@ m->release_C_heap_structures(); } -void InstanceKlass::release_C_heap_structures() { - +// Called also by InstanceKlass::deallocate_contents, with false for release_constant_pool. +void InstanceKlass::release_C_heap_structures(bool release_constant_pool) { // Clean up C heap - release_C_heap_structures_internal(); - constants()->release_C_heap_structures(); + Klass::release_C_heap_structures(); // Deallocate and call destructors for MDO mutexes methods_do(method_release_C_heap_structures); -} - -void InstanceKlass::release_C_heap_structures_internal() { - Klass::release_C_heap_structures(); - - // Can't release the constant pool here because the constant pool can be - // deallocated separately from the InstanceKlass for default methods and - // redefine classes. // Deallocate oop map cache if (_oop_map_cache != NULL) { @@ -2692,6 +2676,10 @@ #endif FREE_C_HEAP_ARRAY(char, _source_debug_extension); + + if (release_constant_pool) { + constants()->release_C_heap_structures(); + } } void InstanceKlass::set_source_debug_extension(const char* array, int length) { @@ -3956,8 +3944,6 @@ // so will be deallocated during the next phase of class unloading. log_trace(redefine, class, iklass, purge) ("previous version " INTPTR_FORMAT " is dead.", p2i(pv_node)); - // For debugging purposes. - pv_node->set_is_scratch_class(); // Unlink from previous version list. assert(pv_node->class_loader_data() == loader_data, "wrong loader_data"); InstanceKlass* next = pv_node->previous_versions(); @@ -4072,8 +4058,6 @@ ConstantPool* cp_ref = scratch_class->constants(); if (!cp_ref->on_stack()) { log_trace(redefine, class, iklass, add)("scratch class not added; no methods are running"); - // For debugging purposes. - scratch_class->set_is_scratch_class(); scratch_class->class_loader_data()->add_to_deallocate_list(scratch_class); return; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/instanceKlass.hpp openjdk-17-17.0.3+7/src/hotspot/share/oops/instanceKlass.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/instanceKlass.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/instanceKlass.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -254,8 +254,7 @@ _misc_is_shared_platform_class = 1 << 11, // defining class loader is platform class loader _misc_is_shared_app_class = 1 << 12, // defining class loader is app class loader _misc_has_resolved_methods = 1 << 13, // resolved methods table entries added for this class - _misc_is_being_redefined = 1 << 14, // used for locking redefinition - _misc_has_contended_annotations = 1 << 15 // has @Contended annotation + _misc_has_contended_annotations = 1 << 14 // has @Contended annotation }; u2 shared_loader_type_bits() const { return _misc_is_shared_boot_class|_misc_is_shared_platform_class|_misc_is_shared_app_class; @@ -738,14 +737,16 @@ #if INCLUDE_JVMTI // Redefinition locking. Class can only be redefined by one thread at a time. + // The flag is in access_flags so that it can be set and reset using atomic + // operations, and not be reset by other misc_flag settings. bool is_being_redefined() const { - return ((_misc_flags & _misc_is_being_redefined) != 0); + return _access_flags.is_being_redefined(); } void set_is_being_redefined(bool value) { if (value) { - _misc_flags |= _misc_is_being_redefined; + _access_flags.set_is_being_redefined(); } else { - _misc_flags &= ~_misc_is_being_redefined; + _access_flags.clear_is_being_redefined(); } } @@ -1107,7 +1108,7 @@ // callbacks for actions during class unloading static void unload_class(InstanceKlass* ik); - virtual void release_C_heap_structures(); + virtual void release_C_heap_structures(bool release_constant_pool = true); // Naming const char* signature_name() const; @@ -1203,8 +1204,6 @@ void initialize_impl (TRAPS); void initialize_super_interfaces (TRAPS); void eager_initialize_impl (); - /* jni_id_for_impl for jfieldID only */ - JNIid* jni_id_for_impl (int offset); // find a local method (returns NULL if not found) Method* find_method_impl(const Symbol* name, @@ -1220,9 +1219,6 @@ StaticLookupMode static_mode, PrivateLookupMode private_mode); - // Free CHeap allocated fields. - void release_C_heap_structures_internal(); - #if INCLUDE_JVMTI // RedefineClasses support void link_previous_versions(InstanceKlass* pv) { _previous_versions = pv; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/klass.cpp openjdk-17-17.0.3+7/src/hotspot/share/oops/klass.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/klass.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/klass.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -105,7 +105,7 @@ return false; } -void Klass::release_C_heap_structures() { +void Klass::release_C_heap_structures(bool release_constant_pool) { if (_name != NULL) _name->decrement_refcount(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/klass.hpp openjdk-17-17.0.3+7/src/hotspot/share/oops/klass.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/klass.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/klass.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -117,8 +117,8 @@ // Klass identifier used to implement devirtualized oop closure dispatching. const KlassID _id; - // vtable length - int _vtable_len; + // Processed access flags, for use by Class.getModifiers. + jint _modifier_flags; // The fields _super_check_offset, _secondary_super_cache, _secondary_supers // and _primary_supers all help make fast subtype checks. See big discussion @@ -154,7 +154,10 @@ // Provide access the corresponding instance java.lang.ClassLoader. ClassLoaderData* _class_loader_data; - jint _modifier_flags; // Processed access flags, for use by Class.getModifiers. + int _vtable_len; // vtable length. This field may be read very often when we + // have lots of itable dispatches (e.g., lambdas and streams). + // Keep it away from the beginning of a Klass to avoid cacheline + // contention that may happen when a nearby object is modified. AccessFlags _access_flags; // Access flags. The class/interface distinction is stored here. JFR_ONLY(DEFINE_TRACE_ID_FIELD;) @@ -693,7 +696,7 @@ Symbol* name() const { return _name; } void set_name(Symbol* n); - virtual void release_C_heap_structures(); + virtual void release_C_heap_structures(bool release_constant_pool = true); public: virtual jint compute_modifier_flags() const = 0; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/method.cpp openjdk-17-17.0.3+7/src/hotspot/share/oops/method.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/method.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/method.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -324,14 +324,11 @@ if (is_native() && bcp == 0) { return 0; } -#ifdef ASSERT - { - ResourceMark rm; - assert(is_native() && bcp == code_base() || contains(bcp) || VMError::is_error_reported(), - "bcp doesn't belong to this method: bcp: " INTPTR_FORMAT ", method: %s", - p2i(bcp), name_and_sig_as_C_string()); - } -#endif + // Do not have a ResourceMark here because AsyncGetCallTrace stack walking code + // may call this after interrupting a nested ResourceMark. + assert(is_native() && bcp == code_base() || contains(bcp) || VMError::is_error_reported(), + "bcp doesn't belong to this method. bcp: " INTPTR_FORMAT, p2i(bcp)); + return bcp - code_base(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/methodData.hpp openjdk-17-17.0.3+7/src/hotspot/share/oops/methodData.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/methodData.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/methodData.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -30,6 +30,7 @@ #include "oops/method.hpp" #include "oops/oop.hpp" #include "runtime/atomic.hpp" +#include "runtime/deoptimization.hpp" #include "runtime/mutex.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" @@ -1965,7 +1966,7 @@ // Whole-method sticky bits and flags enum { - _trap_hist_limit = 25 JVMCI_ONLY(+5), // decoupled from Deoptimization::Reason_LIMIT + _trap_hist_limit = Deoptimization::Reason_TRAP_HISTORY_LENGTH, _trap_hist_mask = max_jubyte, _extra_data_count = 4 // extra DataLayout headers, for trap history }; // Public flag values @@ -1980,6 +1981,7 @@ uint _nof_overflow_traps; // trap count, excluding _trap_hist union { intptr_t _align; + // JVMCI separates trap history for OSR compilations from normal compilations u1 _array[JVMCI_ONLY(2 *) MethodData::_trap_hist_limit]; } _trap_hist; @@ -1996,14 +1998,14 @@ // Return (uint)-1 for overflow. uint trap_count(int reason) const { - assert((uint)reason < JVMCI_ONLY(2*) _trap_hist_limit, "oob"); + assert((uint)reason < ARRAY_SIZE(_trap_hist._array), "oob"); return (int)((_trap_hist._array[reason]+1) & _trap_hist_mask) - 1; } uint inc_trap_count(int reason) { // Count another trap, anywhere in this method. assert(reason >= 0, "must be single trap"); - assert((uint)reason < JVMCI_ONLY(2*) _trap_hist_limit, "oob"); + assert((uint)reason < ARRAY_SIZE(_trap_hist._array), "oob"); uint cnt1 = 1 + _trap_hist._array[reason]; if ((cnt1 & _trap_hist_mask) != 0) { // if no counter overflow... _trap_hist._array[reason] = cnt1; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/buildOopMap.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/buildOopMap.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/buildOopMap.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/buildOopMap.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -231,10 +231,6 @@ VMReg r = OptoReg::as_VMReg(OptoReg::Name(reg), framesize, max_inarg_slot); - if (false && r->is_reg() && !r->is_concrete()) { - continue; - } - // See if dead (no reaching def). Node *def = _defs[reg]; // Get reaching def assert( def, "since live better have reaching def" ); @@ -312,14 +308,10 @@ set_live_bit(live,breg); // Already missed our turn? if( breg < reg ) { - if (b->is_stack() || b->is_concrete() || true ) { - omap->set_oop( b); - } + omap->set_oop(b); } } - if (b->is_stack() || b->is_concrete() || true ) { - omap->set_derived_oop( r, b); - } + omap->set_derived_oop(r, b); } } else if( t->isa_narrowoop() ) { @@ -347,9 +339,7 @@ assert( dup_check[_callees[reg]]==0, "trying to callee save same reg twice" ); debug_only( dup_check[_callees[reg]]=1; ) VMReg callee = OptoReg::as_VMReg(OptoReg::Name(_callees[reg])); - if ( callee->is_concrete() || true ) { - omap->set_callee_saved( r, callee); - } + omap->set_callee_saved(r, callee); } else { // Other - some reaching non-oop value diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/cfgnode.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/cfgnode.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/cfgnode.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/cfgnode.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1366,9 +1366,12 @@ } int true_path = is_diamond_phi(); - if (true_path != 0) { + // Delay CMove'ing identity if Ideal has not had the chance to handle unsafe cases, yet. + if (true_path != 0 && !(phase->is_IterGVN() && wait_for_region_igvn(phase))) { Node* id = is_cmove_id(phase, true_path); - if (id != NULL) return id; + if (id != NULL) { + return id; + } } // Looking for phis with identical inputs. If we find one that has @@ -2269,13 +2272,13 @@ // Phi(...MergeMem(m0, m1:AT1, m2:AT2)...) into // MergeMem(Phi(...m0...), Phi:AT1(...m1...), Phi:AT2(...m2...)) PhaseIterGVN* igvn = phase->is_IterGVN(); + assert(igvn != NULL, "sanity check"); Node* hook = new Node(1); PhiNode* new_base = (PhiNode*) clone(); // Must eagerly register phis, since they participate in loops. - if (igvn) { - igvn->register_new_node_with_optimizer(new_base); - hook->add_req(new_base); - } + igvn->register_new_node_with_optimizer(new_base); + hook->add_req(new_base); + MergeMemNode* result = MergeMemNode::make(new_base); for (uint i = 1; i < req(); ++i) { Node *ii = in(i); @@ -2287,10 +2290,8 @@ if (mms.is_empty()) { Node* new_phi = new_base->slice_memory(mms.adr_type(phase->C)); made_new_phi = true; - if (igvn) { - igvn->register_new_node_with_optimizer(new_phi); - hook->add_req(new_phi); - } + igvn->register_new_node_with_optimizer(new_phi); + hook->add_req(new_phi); mms.set_memory(new_phi); } Node* phi = mms.memory(); @@ -2308,6 +2309,13 @@ } } } + // Already replace this phi node to cut it off from the graph to not interfere in dead loop checks during the + // transformations of the new phi nodes below. Otherwise, we could wrongly conclude that there is no dead loop + // because we are finding this phi node again. Also set the type of the new MergeMem node in case we are also + // visiting it in the transformations below. + igvn->replace_node(this, result); + igvn->set_type(result, result->bottom_type()); + // now transform the new nodes, and return the mergemem for (MergeMemStream mms(result); mms.next_non_empty(); ) { Node* phi = mms.memory(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/library_call.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/library_call.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/library_call.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/library_call.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1561,7 +1561,7 @@ if (is_store) { access_store_at(value, adr, TypeAryPtr::BYTES, ch, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED); } else { - ch = access_load_at(value, adr, TypeAryPtr::BYTES, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); + ch = access_load_at(value, adr, TypeAryPtr::BYTES, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD | C2_UNKNOWN_CONTROL_LOAD); set_result(ch); } return true; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopPredicate.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopPredicate.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopPredicate.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopPredicate.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -341,9 +341,9 @@ assert(predicate->is_Proj() && predicate->as_Proj()->is_IfProj(), "predicate must be a projection of an if node"); IfProjNode* predicate_proj = predicate->as_IfProj(); - ProjNode* fast_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, uncommon_proj, reason, iffast_pred, loop); + ProjNode* fast_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, reason, iffast_pred); assert(skeleton_predicate_has_opaque(fast_proj->in(0)->as_If()), "must find skeleton predicate for fast loop"); - ProjNode* slow_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, uncommon_proj, reason, ifslow_pred, loop); + ProjNode* slow_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, reason, ifslow_pred); assert(skeleton_predicate_has_opaque(slow_proj->in(0)->as_If()), "must find skeleton predicate for slow loop"); // Update control dependent data nodes. @@ -397,10 +397,10 @@ // Clone a skeleton predicate for an unswitched loop. OpaqueLoopInit and OpaqueLoopStride nodes are cloned and uncommon // traps are kept for the predicate (a Halt node is used later when creating pre/main/post loops and copying this cloned // predicate again). -ProjNode* PhaseIdealLoop::clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, Node* uncommon_proj, - Deoptimization::DeoptReason reason, ProjNode* output_proj, - IdealLoopTree* loop) { - Node* bol = clone_skeleton_predicate_bool(iff, NULL, NULL, predicate, uncommon_proj, output_proj, loop); +ProjNode* PhaseIdealLoop::clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, + Deoptimization::DeoptReason reason, + ProjNode* output_proj) { + Node* bol = clone_skeleton_predicate_bool(iff, NULL, NULL, output_proj); ProjNode* proj = create_new_if_for_predicate(output_proj, NULL, reason, iff->Opcode(), predicate->is_IfTrue()); _igvn.replace_input_of(proj->in(0), 1, bol); _igvn.replace_input_of(output_proj->in(0), 0, proj); @@ -816,7 +816,7 @@ BoolNode* PhaseIdealLoop::rc_predicate(IdealLoopTree *loop, Node* ctrl, int scale, Node* offset, Node* init, Node* limit, jint stride, - Node* range, bool upper, bool &overflow) { + Node* range, bool upper, bool &overflow, bool negate) { jint con_limit = (limit != NULL && limit->is_Con()) ? limit->get_int() : 0; jint con_init = init->is_Con() ? init->get_int() : 0; jint con_offset = offset->is_Con() ? offset->get_int() : 0; @@ -942,7 +942,7 @@ cmp = new CmpUNode(max_idx_expr, range); } register_new_node(cmp, ctrl); - BoolNode* bol = new BoolNode(cmp, BoolTest::lt); + BoolNode* bol = new BoolNode(cmp, negate ? BoolTest::ge : BoolTest::lt); register_new_node(bol, ctrl); if (TraceLoopPredicate) { @@ -1309,36 +1309,26 @@ } // If predicate expressions may overflow in the integer range, longs are used. bool overflow = false; + bool negate = (proj->_con != predicate_proj->_con); // Test the lower bound - BoolNode* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false, overflow); - // Negate test if necessary - bool negated = false; - if (proj->_con != predicate_proj->_con) { - lower_bound_bol = new BoolNode(lower_bound_bol->in(1), lower_bound_bol->_test.negate()); - register_new_node(lower_bound_bol, ctrl); - negated = true; - } + BoolNode* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false, overflow, negate); + ProjNode* lower_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, reason, overflow ? Op_If : iff->Opcode()); IfNode* lower_bound_iff = lower_bound_proj->in(0)->as_If(); _igvn.hash_delete(lower_bound_iff); lower_bound_iff->set_req(1, lower_bound_bol); - if (TraceLoopPredicate) tty->print_cr("lower bound check if: %s %d ", negated ? " negated" : "", lower_bound_iff->_idx); + if (TraceLoopPredicate) tty->print_cr("lower bound check if: %s %d ", negate ? " negated" : "", lower_bound_iff->_idx); // Test the upper bound - BoolNode* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true, overflow); - negated = false; - if (proj->_con != predicate_proj->_con) { - upper_bound_bol = new BoolNode(upper_bound_bol->in(1), upper_bound_bol->_test.negate()); - register_new_node(upper_bound_bol, ctrl); - negated = true; - } + BoolNode* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true, overflow, negate); + ProjNode* upper_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, reason, overflow ? Op_If : iff->Opcode()); assert(upper_bound_proj->in(0)->as_If()->in(0) == lower_bound_proj, "should dominate"); IfNode* upper_bound_iff = upper_bound_proj->in(0)->as_If(); _igvn.hash_delete(upper_bound_iff); upper_bound_iff->set_req(1, upper_bound_bol); - if (TraceLoopPredicate) tty->print_cr("upper bound check if: %s %d ", negated ? " negated" : "", lower_bound_iff->_idx); + if (TraceLoopPredicate) tty->print_cr("upper bound check if: %s %d ", negate ? " negated" : "", lower_bound_iff->_idx); // Fall through into rest of the clean up code which will move // any dependent nodes onto the upper bound test. @@ -1384,10 +1374,10 @@ Node* rng, bool &overflow, Deoptimization::DeoptReason reason) { // First predicate for the initial value on first loop iteration - assert(proj->_con && predicate_proj->_con, "not a range check?"); Node* opaque_init = new OpaqueLoopInitNode(C, init); register_new_node(opaque_init, upper_bound_proj); - BoolNode* bol = rc_predicate(loop, upper_bound_proj, scale, offset, opaque_init, limit, stride, rng, (stride > 0) != (scale > 0), overflow); + bool negate = (proj->_con != predicate_proj->_con); + BoolNode* bol = rc_predicate(loop, upper_bound_proj, scale, offset, opaque_init, limit, stride, rng, (stride > 0) != (scale > 0), overflow, negate); Node* opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1)); // This will go away once loop opts are over C->add_skeleton_predicate_opaq(opaque_bol); register_new_node(opaque_bol, upper_bound_proj); @@ -1405,7 +1395,7 @@ register_new_node(max_value, new_proj); max_value = new AddINode(opaque_init, max_value); register_new_node(max_value, new_proj); - bol = rc_predicate(loop, new_proj, scale, offset, max_value, limit, stride, rng, (stride > 0) != (scale > 0), overflow); + bol = rc_predicate(loop, new_proj, scale, offset, max_value, limit, stride, rng, (stride > 0) != (scale > 0), overflow, negate); opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1)); C->add_skeleton_predicate_opaq(opaque_bol); register_new_node(opaque_bol, new_proj); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopTransform.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopTransform.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopTransform.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopTransform.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1264,10 +1264,12 @@ // Clone the skeleton predicate twice and initialize one with the initial // value of the loop induction variable. Leave the other predicate // to be initialized when increasing the stride during loop unrolling. - prev_proj = clone_skeleton_predicate_for_main_loop(iff, opaque_init, NULL, predicate, uncommon_proj, current_proj, outer_loop, prev_proj); + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, opaque_init, NULL, predicate, uncommon_proj, + current_proj, outer_loop, prev_proj); assert(skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), ""); - prev_proj = clone_skeleton_predicate_for_main_loop(iff, init, stride, predicate, uncommon_proj, current_proj, outer_loop, prev_proj); + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, init, stride, predicate, uncommon_proj, + current_proj, outer_loop, prev_proj); assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), ""); // Rewire any control inputs from the cloned skeleton predicates down to the main and post loop for data nodes that are part of the @@ -1344,8 +1346,7 @@ // Clone the skeleton predicate bool for a main or unswitched loop: // Main loop: Set new_init and new_stride nodes as new inputs. // Unswitched loop: new_init and new_stride are both NULL. Clone OpaqueLoopInit and OpaqueLoopStride instead. -Node* PhaseIdealLoop::clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, - Node* control, IdealLoopTree* outer_loop) { +Node* PhaseIdealLoop::clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* control) { Node_Stack to_clone(2); to_clone.push(iff->in(1), 1); uint current = C->unique(); @@ -1421,9 +1422,9 @@ // Clone a skeleton predicate for the main loop. new_init and new_stride are set as new inputs. Since the predicates cannot fail at runtime, // Halt nodes are inserted instead of uncommon traps. -Node* PhaseIdealLoop::clone_skeleton_predicate_for_main_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, - Node* control, IdealLoopTree* outer_loop, Node* input_proj) { - Node* result = clone_skeleton_predicate_bool(iff, new_init, new_stride, predicate, uncommon_proj, control, outer_loop); +Node* PhaseIdealLoop::clone_skeleton_predicate_for_main_or_post_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, + Node* control, IdealLoopTree* outer_loop, Node* input_proj) { + Node* result = clone_skeleton_predicate_bool(iff, new_init, new_stride, control); Node* proj = predicate->clone(); Node* other_proj = uncommon_proj->clone(); Node* new_iff = iff->clone(); @@ -1437,8 +1438,8 @@ C->root()->add_req(halt); new_iff->set_req(0, input_proj); - register_control(new_iff, outer_loop->_parent, input_proj); - register_control(proj, outer_loop->_parent, new_iff); + register_control(new_iff, outer_loop == _ltree_root ? _ltree_root : outer_loop->_parent, input_proj); + register_control(proj, outer_loop == _ltree_root ? _ltree_root : outer_loop->_parent, new_iff); register_control(other_proj, _ltree_root, new_iff); register_control(halt, _ltree_root, other_proj); return proj; @@ -1521,7 +1522,8 @@ // Add the post loop const uint idx_before_pre_post = Compile::current()->unique(); CountedLoopNode *post_head = NULL; - Node *main_exit = insert_post_loop(loop, old_new, main_head, main_end, incr, limit, post_head); + Node* post_incr = incr; + Node* main_exit = insert_post_loop(loop, old_new, main_head, main_end, post_incr, limit, post_head); const uint idx_after_post_before_pre = Compile::current()->unique(); //------------------------------ @@ -1620,6 +1622,7 @@ assert(post_head->in(1)->is_IfProj(), "must be zero-trip guard If node projection of the post loop"); copy_skeleton_predicates_to_main_loop(pre_head, castii, stride, outer_loop, outer_main_head, dd_main_head, idx_before_pre_post, idx_after_post_before_pre, min_taken, post_head->in(1), old_new); + copy_skeleton_predicates_to_post_loop(outer_main_head, post_head, post_incr, stride); // Step B4: Shorten the pre-loop to run only 1 iteration (for now). // RCE and alignment may change this later. @@ -1742,6 +1745,7 @@ // In this case we throw away the result as we are not using it to connect anything else. CountedLoopNode *post_head = NULL; insert_post_loop(loop, old_new, main_head, main_end, incr, limit, post_head); + copy_skeleton_predicates_to_post_loop(main_head->skip_strip_mined(), post_head, incr, main_head->stride()); // It's difficult to be precise about the trip-counts // for post loops. They are usually very short, @@ -1788,6 +1792,7 @@ // In this case we throw away the result as we are not using it to connect anything else. CountedLoopNode *post_head = NULL; insert_post_loop(loop, old_new, main_head, main_end, incr, limit, post_head); + copy_skeleton_predicates_to_post_loop(main_head->skip_strip_mined(), post_head, incr, main_head->stride()); // It's difficult to be precise about the trip-counts // for post loops. They are usually very short, @@ -1804,9 +1809,9 @@ //------------------------------insert_post_loop------------------------------- // Insert post loops. Add a post loop to the given loop passed. -Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new, - CountedLoopNode *main_head, CountedLoopEndNode *main_end, - Node *incr, Node *limit, CountedLoopNode *&post_head) { +Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, + CountedLoopNode* main_head, CountedLoopEndNode* main_end, + Node*& incr, Node* limit, CountedLoopNode*& post_head) { IfNode* outer_main_end = main_end; IdealLoopTree* outer_loop = loop; if (main_head->is_strip_mined()) { @@ -1890,8 +1895,8 @@ } // CastII for the new post loop: - Node* castii = cast_incr_before_loop(zer_opaq->in(1), zer_taken, post_head); - assert(castii != NULL, "no castII inserted"); + incr = cast_incr_before_loop(zer_opaq->in(1), zer_taken, post_head); + assert(incr != NULL, "no castII inserted"); return new_main_exit; } @@ -1933,7 +1938,8 @@ _igvn.replace_input_of(iff, 1, iff->in(1)->in(2)); } else { // Add back predicates updated for the new stride. - prev_proj = clone_skeleton_predicate_for_main_loop(iff, init, max_value, entry, proj, ctrl, outer_loop, prev_proj); + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, init, max_value, entry, proj, ctrl, outer_loop, + prev_proj); assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), "unexpected"); } } @@ -1945,6 +1951,34 @@ } } +void PhaseIdealLoop::copy_skeleton_predicates_to_post_loop(LoopNode* main_loop_head, CountedLoopNode* post_loop_head, Node* init, Node* stride) { + // Go over the skeleton predicates of the main loop and make a copy for the post loop with its initial iv value and + // stride as inputs. + Node* post_loop_entry = post_loop_head->in(LoopNode::EntryControl); + Node* main_loop_entry = main_loop_head->in(LoopNode::EntryControl); + IdealLoopTree* post_loop = get_loop(post_loop_head); + + Node* ctrl = main_loop_entry; + Node* prev_proj = post_loop_entry; + while (ctrl != NULL && ctrl->is_Proj() && ctrl->in(0)->is_If()) { + IfNode* iff = ctrl->in(0)->as_If(); + ProjNode* proj = iff->proj_out(1 - ctrl->as_Proj()->_con); + if (proj->unique_ctrl_out()->Opcode() != Op_Halt) { + break; + } + if (iff->in(1)->Opcode() == Op_Opaque4 && skeleton_predicate_has_opaque(iff)) { + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, init, stride, ctrl, proj, post_loop_entry, + post_loop, prev_proj); + assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), "unexpected"); + } + ctrl = ctrl->in(0)->in(0); + } + if (prev_proj != post_loop_entry) { + _igvn.replace_input_of(post_loop_head, LoopNode::EntryControl, prev_proj); + set_idom(post_loop_head, prev_proj, dom_depth(post_loop_head)); + } +} + //------------------------------do_unroll-------------------------------------- // Unroll the loop body one step - make each trip do 2 iterations. void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adjust_min_trip) { @@ -2555,7 +2589,7 @@ Node* predicate_proj, int scale_con, Node* offset, Node* limit, jint stride_con, Node* value) { bool overflow = false; - BoolNode* bol = rc_predicate(loop, predicate_proj, scale_con, offset, value, NULL, stride_con, limit, (stride_con > 0) != (scale_con > 0), overflow); + BoolNode* bol = rc_predicate(loop, predicate_proj, scale_con, offset, value, NULL, stride_con, limit, (stride_con > 0) != (scale_con > 0), overflow, false); Node* opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1)); register_new_node(opaque_bol, predicate_proj); IfNode* new_iff = NULL; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopnode.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopnode.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopnode.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopnode.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2150,12 +2150,14 @@ } Node* CountedLoopNode::skip_predicates() { + Node* ctrl = in(LoopNode::EntryControl); if (is_main_loop()) { - Node* ctrl = skip_strip_mined()->in(LoopNode::EntryControl); - + ctrl = skip_strip_mined()->in(LoopNode::EntryControl); + } + if (is_main_loop() || is_post_loop()) { return skip_predicates_from_entry(ctrl); } - return in(LoopNode::EntryControl); + return ctrl; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopnode.hpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopnode.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopnode.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopnode.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -915,13 +915,13 @@ void copy_skeleton_predicates_to_main_loop(CountedLoopNode* pre_head, Node* init, Node* stride, IdealLoopTree* outer_loop, LoopNode* outer_main_head, uint dd_main_head, const uint idx_before_pre_post, const uint idx_after_post_before_pre, Node* zero_trip_guard_proj_main, Node* zero_trip_guard_proj_post, const Node_List &old_new); - Node* clone_skeleton_predicate_for_main_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control, - IdealLoopTree* outer_loop, Node* input_proj); - Node* clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control, - IdealLoopTree* outer_loop); + Node* clone_skeleton_predicate_for_main_or_post_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control, + IdealLoopTree* outer_loop, Node* input_proj); + Node* clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* control); static bool skeleton_predicate_has_opaque(IfNode* iff); static void get_skeleton_predicates(Node* predicate, Unique_Node_List& list, bool get_opaque = false); void update_main_loop_skeleton_predicates(Node* ctrl, CountedLoopNode* loop_head, Node* init, int stride_con); + void copy_skeleton_predicates_to_post_loop(LoopNode* main_loop_head, CountedLoopNode* post_loop_head, Node* init, Node* stride); void insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol); #ifdef ASSERT bool only_has_infinite_loops(); @@ -1244,9 +1244,9 @@ void insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_new, bool peel_only ); // Add post loop after the given loop. - Node *insert_post_loop(IdealLoopTree *loop, Node_List &old_new, - CountedLoopNode *main_head, CountedLoopEndNode *main_end, - Node *incr, Node *limit, CountedLoopNode *&post_head); + Node *insert_post_loop(IdealLoopTree* loop, Node_List& old_new, + CountedLoopNode* main_head, CountedLoopEndNode* main_end, + Node*& incr, Node* limit, CountedLoopNode*& post_head); // Add an RCE'd post loop which we will multi-version adapt for run time test path usage void insert_scalar_rced_post_loop( IdealLoopTree *loop, Node_List &old_new ); @@ -1303,7 +1303,8 @@ BoolNode* rc_predicate(IdealLoopTree *loop, Node* ctrl, int scale, Node* offset, Node* init, Node* limit, jint stride, - Node* range, bool upper, bool &overflow); + Node* range, bool upper, bool &overflow, + bool negate); // Implementation of the loop predication to promote checks outside the loop bool loop_predication_impl(IdealLoopTree *loop); @@ -1579,8 +1580,9 @@ Node_List* old_new = NULL); void clone_skeleton_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, Deoptimization::DeoptReason reason, ProjNode* old_predicate_proj, ProjNode* iffast_pred, ProjNode* ifslow_pred); - ProjNode* clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, Node* uncommon_proj, Deoptimization::DeoptReason reason, - ProjNode* output_proj, IdealLoopTree* loop); + ProjNode* clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, + Deoptimization::DeoptReason reason, + ProjNode* output_proj); static void check_created_predicate_for_unswitching(const Node* new_entry) PRODUCT_RETURN; bool _created_loop_node; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopopts.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopopts.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopopts.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopopts.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1174,6 +1174,16 @@ if (!n->in(0)->is_Region()) { return false; } + + IfNode* n_if = n->as_If(); + if (n_if->proj_out(0)->outcnt() > 1 || n_if->proj_out(1)->outcnt() > 1) { + // Removing the dominated If node by using the split-if optimization does not work if there are data dependencies. + // Some data dependencies depend on its immediate dominator If node and should not be separated from it (e.g. null + // checks, division by zero checks etc.). Bail out for now until data dependencies are correctly handled when + // optimizing back-to-back ifs. + return false; + } + Node* region = n->in(0); Node* dom = idom(region); if (!dom->is_If() || dom->in(1) != n->in(1)) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/macro.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/macro.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/macro.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/macro.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2562,6 +2562,7 @@ assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_Opaque2 || n->Opcode() == Op_Opaque3 || + n->Opcode() == Op_Opaque4 || BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(n), "unknown node type in macro list"); } @@ -2623,6 +2624,19 @@ _igvn.replace_node(n, repl); success = true; #endif + } else if (n->Opcode() == Op_Opaque4) { + // With Opaque4 nodes, the expectation is that the test of input 1 + // is always equal to the constant value of input 2. So we can + // remove the Opaque4 and replace it by input 2. In debug builds, + // leave the non constant test in instead to sanity check that it + // never fails (if it does, that subgraph was constructed so, at + // runtime, a Halt node is executed). +#ifdef ASSERT + _igvn.replace_node(n, n->in(1)); +#else + _igvn.replace_node(n, n->in(2)); +#endif + success = true; } else if (n->Opcode() == Op_OuterStripMinedLoop) { n->as_OuterStripMinedLoop()->adjust_strip_mined_loop(&_igvn); C->remove_macro_node(n); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/mulnode.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/mulnode.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/mulnode.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/mulnode.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -59,13 +59,11 @@ // We also canonicalize the Node, moving constants to the right input, // and flatten expressions (so that 1+x+2 becomes x+3). Node *MulNode::Ideal(PhaseGVN *phase, bool can_reshape) { - const Type *t1 = phase->type( in(1) ); - const Type *t2 = phase->type( in(2) ); - Node *progress = NULL; // Progress flag + Node* in1 = in(1); + Node* in2 = in(2); + Node* progress = NULL; // Progress flag // convert "max(a,b) * min(a,b)" into "a*b". - Node *in1 = in(1); - Node *in2 = in(2); if ((in(1)->Opcode() == max_opcode() && in(2)->Opcode() == min_opcode()) || (in(1)->Opcode() == min_opcode() && in(2)->Opcode() == max_opcode())) { Node *in11 = in(1)->in(1); @@ -83,10 +81,15 @@ igvn->_worklist.push(in1); igvn->_worklist.push(in2); } + in1 = in(1); + in2 = in(2); progress = this; } } + const Type* t1 = phase->type(in1); + const Type* t2 = phase->type(in2); + // We are OK if right is a constant, or right is a load and // left is a non-constant. if( !(t2->singleton() || diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/opaquenode.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/opaquenode.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/opaquenode.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/opaquenode.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -60,25 +60,6 @@ return (&n == this); // Always fail except on self } -Node* Opaque4Node::Identity(PhaseGVN* phase) { - if (phase->C->post_loop_opts_phase()) { - // With Opaque4 nodes, the expectation is that the test of input 1 - // is always equal to the constant value of input 2. So we can - // remove the Opaque4 and replace it by input 2. In debug builds, - // leave the non constant test in instead to sanity check that it - // never fails (if it does, that subgraph was constructed so, at - // runtime, a Halt node is executed). -#ifdef ASSERT - return this->in(1); -#else - return this->in(2); -#endif - } else { - phase->C->record_for_post_loop_opts_igvn(this); - } - return this; -} - const Type* Opaque4Node::Value(PhaseGVN* phase) const { return phase->type(in(1)); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/opaquenode.hpp openjdk-17-17.0.3+7/src/hotspot/share/opto/opaquenode.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/opaquenode.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/opaquenode.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -114,11 +114,13 @@ // GraphKit::must_be_not_null(). class Opaque4Node : public Node { public: - Opaque4Node(Compile* C, Node *tst, Node* final_tst) : Node(NULL, tst, final_tst) {} + Opaque4Node(Compile* C, Node *tst, Node* final_tst) : Node(NULL, tst, final_tst) { + init_flags(Flag_is_macro); + C->add_macro_node(this); + } virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::BOOL; } - virtual Node* Identity(PhaseGVN* phase); virtual const Type* Value(PhaseGVN* phase) const; }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/output.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/output.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/output.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/output.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2911,6 +2911,16 @@ if( !OptoReg::is_valid(def_reg) ) // Ignore stores & control flow return; + if (OptoReg::is_reg(def_reg)) { + VMReg vmreg = OptoReg::as_VMReg(def_reg); + if (vmreg->is_reg() && !vmreg->is_concrete() && !vmreg->prev()->is_concrete()) { + // This is one of the high slots of a vector register. + // ScheduleAndBundle already checked there are no live wide + // vectors in this method so it can be safely ignored. + return; + } + } + Node *pinch = _reg_node[def_reg]; // Get pinch point if ((pinch == NULL) || _cfg->get_block_for_node(pinch) != b || // No pinch-point yet? is_def ) { // Check for a true def (not a kill) diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/prims/jvmtiEnvBase.cpp openjdk-17-17.0.3+7/src/hotspot/share/prims/jvmtiEnvBase.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/prims/jvmtiEnvBase.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/prims/jvmtiEnvBase.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -830,7 +830,7 @@ "call by myself / at safepoint / at handshake"); int count = 0; if (java_thread->has_last_Java_frame()) { - RegisterMap reg_map(java_thread); + RegisterMap reg_map(java_thread, false /* update_map */, false /* process_frames */); ResourceMark rm(current_thread); javaVFrame *jvf = java_thread->last_java_vframe(®_map); HandleMark hm(current_thread); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/prims/jvmtiRedefineClasses.cpp openjdk-17-17.0.3+7/src/hotspot/share/prims/jvmtiRedefineClasses.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/prims/jvmtiRedefineClasses.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/prims/jvmtiRedefineClasses.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -4419,6 +4419,9 @@ the_class->set_has_been_redefined(); + // Scratch class is unloaded but still needs cleaning, and skipping for CDS. + scratch_class->set_is_scratch_class(); + // keep track of previous versions of this class the_class->add_previous_version(scratch_class, emcp_method_count); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/prims/vectorSupport.cpp openjdk-17-17.0.3+7/src/hotspot/share/prims/vectorSupport.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/prims/vectorSupport.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/prims/vectorSupport.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -291,6 +291,7 @@ case T_BYTE: // fall-through case T_SHORT: // fall-through case T_INT: return Op_NegI; + case T_LONG: return Op_NegL; case T_FLOAT: return Op_NegF; case T_DOUBLE: return Op_NegD; default: fatal("NEG: %s", type2name(bt)); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/prims/whitebox.cpp openjdk-17-17.0.3+7/src/hotspot/share/prims/whitebox.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/prims/whitebox.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/prims/whitebox.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -995,7 +995,7 @@ const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags) { - CgroupInfo cg_infos[4]; + CgroupInfo cg_infos[CG_INFO_LENGTH]; return CgroupSubsystemFactory::determine_type(cg_infos, proc_cgroups, proc_self_cgroup, proc_self_mountinfo, cg_flags); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/runtime/deoptimization.cpp openjdk-17-17.0.3+7/src/hotspot/share/runtime/deoptimization.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/runtime/deoptimization.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/runtime/deoptimization.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -775,6 +775,7 @@ // at an uncommon trap for an invoke (where the compiler // generates debug info before the invoke has executed) Bytecodes::Code cur_code = str.next(); + Bytecodes::Code next_code = Bytecodes::_shouldnotreachhere; if (Bytecodes::is_invoke(cur_code)) { Bytecode_invoke invoke(mh, iframe->interpreter_frame_bci()); cur_invoke_parameter_size = invoke.size_of_parameters(); @@ -783,7 +784,7 @@ } } if (str.bci() < max_bci) { - Bytecodes::Code next_code = str.next(); + next_code = str.next(); if (next_code >= 0) { // The interpreter oop map generator reports results before // the current bytecode has executed except in the case of @@ -833,6 +834,10 @@ // Print out some information that will help us debug the problem tty->print_cr("Wrong number of expression stack elements during deoptimization"); tty->print_cr(" Error occurred while verifying frame %d (0..%d, 0 is topmost)", i, cur_array->frames() - 1); + tty->print_cr(" Current code %s", Bytecodes::name(cur_code)); + if (try_next_mask) { + tty->print_cr(" Next code %s", Bytecodes::name(next_code)); + } tty->print_cr(" Fabricated interpreter frame had %d expression stack elements", iframe->interpreter_frame_expression_stack_size()); tty->print_cr(" Interpreter oop map had %d expression stack elements", mask.expression_stack_size()); @@ -2384,7 +2389,8 @@ uint idx = reason; #if INCLUDE_JVMCI if (is_osr) { - idx += Reason_LIMIT; + // Upper half of history array used for traps in OSR compilations + idx += Reason_TRAP_HISTORY_LENGTH; } #endif uint prior_trap_count = trap_mdo->trap_count(idx); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/runtime/deoptimization.hpp openjdk-17-17.0.3+7/src/hotspot/share/runtime/deoptimization.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/runtime/deoptimization.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/runtime/deoptimization.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -46,6 +46,7 @@ public: // What condition caused the deoptimization? + // Note: Keep this enum in sync. with Deoptimization::_trap_reason_name. enum DeoptReason { Reason_many = -1, // indicates presence of several reasons Reason_none = 0, // indicates absence of a relevant deopt. @@ -98,20 +99,22 @@ Reason_jsr_mismatch, #endif + // Used to define MethodData::_trap_hist_limit where Reason_tenured isn't included + Reason_TRAP_HISTORY_LENGTH, + // Reason_tenured is counted separately, add normal counted Reasons above. - // Related to MethodData::_trap_hist_limit where Reason_tenured isn't included - Reason_tenured, // age of the code has reached the limit + Reason_tenured = Reason_TRAP_HISTORY_LENGTH, // age of the code has reached the limit Reason_LIMIT, - // Note: Keep this enum in sync. with _trap_reason_name. - Reason_RECORDED_LIMIT = Reason_profile_predicate // some are not recorded per bc // Note: Reason_RECORDED_LIMIT should fit into 31 bits of // DataLayout::trap_bits. This dependency is enforced indirectly // via asserts, to avoid excessive direct header-to-header dependencies. // See Deoptimization::trap_state_reason and class DataLayout. + Reason_RECORDED_LIMIT = Reason_profile_predicate, // some are not recorded per bc }; // What action must be taken by the runtime? + // Note: Keep this enum in sync. with Deoptimization::_trap_action_name. enum DeoptAction { Action_none, // just interpret, do not invalidate nmethod Action_maybe_recompile, // recompile the nmethod; need not invalidate @@ -119,7 +122,6 @@ Action_make_not_entrant, // invalidate the nmethod, recompile (probably) Action_make_not_compilable, // invalidate the nmethod and do not compile Action_LIMIT - // Note: Keep this enum in sync. with _trap_action_name. }; enum { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/runtime/objectMonitor.hpp openjdk-17-17.0.3+7/src/hotspot/share/runtime/objectMonitor.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/runtime/objectMonitor.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/runtime/objectMonitor.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -127,7 +127,7 @@ #define OM_CACHE_LINE_SIZE DEFAULT_CACHE_LINE_SIZE #endif -class ObjectMonitor : public CHeapObj { +class ObjectMonitor : public CHeapObj { friend class ObjectSynchronizer; friend class ObjectWaiter; friend class VMStructs; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/runtime/os.cpp openjdk-17-17.0.3+7/src/hotspot/share/runtime/os.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/runtime/os.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/runtime/os.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -916,7 +916,7 @@ addr = addr2; } } -#endif // HANDLE_FUNCTION_DESCRIPTORS +#endif // HAVE_FUNCTION_DESCRIPTORS if (have_function_name) { // Print function name, optionally demangled diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/services/attachListener.cpp openjdk-17-17.0.3+7/src/hotspot/share/services/attachListener.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/services/attachListener.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/services/attachListener.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -292,6 +292,7 @@ uintx num; if (!Arguments::parse_uintx(num_str, &num, 0)) { out->print_cr("Invalid parallel thread number: [%s]", num_str); + delete fs; return JNI_ERR; } parallel_thread_num = num == 0 ? parallel_thread_num : (uint)num; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/utilities/accessFlags.hpp openjdk-17-17.0.3+7/src/hotspot/share/utilities/accessFlags.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/utilities/accessFlags.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/utilities/accessFlags.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -69,6 +69,7 @@ JVM_ACC_IS_SHARED_CLASS = 0x02000000, // True if klass is shared JVM_ACC_IS_HIDDEN_CLASS = 0x04000000, // True if klass is hidden JVM_ACC_IS_VALUE_BASED_CLASS = 0x08000000, // True if klass is marked as a ValueBased class + JVM_ACC_IS_BEING_REDEFINED = 0x00100000, // True if the klass is being redefined. // Klass* and Method* flags JVM_ACC_HAS_LOCAL_VARIABLE_TABLE= 0x00200000, @@ -159,6 +160,10 @@ void set_has_localvariable_table() { atomic_set_bits(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE); } void clear_has_localvariable_table() { atomic_clear_bits(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE); } + bool is_being_redefined() const { return (_flags & JVM_ACC_IS_BEING_REDEFINED) != 0; } + void set_is_being_redefined() { atomic_set_bits(JVM_ACC_IS_BEING_REDEFINED); } + void clear_is_being_redefined() { atomic_clear_bits(JVM_ACC_IS_BEING_REDEFINED); } + // field flags bool is_field_access_watched() const { return (_flags & JVM_ACC_FIELD_ACCESS_WATCHED) != 0; } bool is_field_modification_watched() const diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/utilities/resourceHash.hpp openjdk-17-17.0.3+7/src/hotspot/share/utilities/resourceHash.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/utilities/resourceHash.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/utilities/resourceHash.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -195,6 +195,32 @@ ++bucket; } } + + // ITER contains bool do_entry(K const&, V const&), which will be + // called for each entry in the table. If do_entry() returns true, + // the entry is deleted. + template + void unlink(ITER* iter) { + Node** bucket = const_cast(_table); + while (bucket < &_table[SIZE]) { + Node** ptr = bucket; + while (*ptr != NULL) { + Node* node = *ptr; + // do_entry must clean up the key and value in Node. + bool clean = iter->do_entry(node->_key, node->_value); + if (clean) { + *ptr = node->_next; + if (ALLOC_TYPE == ResourceObj::C_HEAP) { + delete node; + } + } else { + ptr = &(node->_next); + } + } + ++bucket; + } + } + }; diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Red Hat Inc. + * Copyright (c) 2020, 2021, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -150,6 +150,16 @@ } @Override + public long getPidsMax() { + return subsystem.getPidsMax(); + } + + @Override + public long getPidsCurrent() { + return subsystem.getPidsCurrent(); + } + + @Override public long getBlkIOServiceCount() { return subsystem.getBlkIOServiceCount(); } diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -36,5 +36,13 @@ * has determined that no limit is being imposed. */ public static final long LONG_RETVAL_UNLIMITED = -1; + public static final String MAX_VAL = "max"; + + public static long limitFromString(String strVal) { + if (strVal == null || MAX_VAL.equals(strVal)) { + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; + } + return Long.parseLong(strVal); + } } diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2022-04-19 19:55:43.000000000 +0000 @@ -51,6 +51,7 @@ private static final String CPUSET_CTRL = "cpuset"; private static final String BLKIO_CTRL = "blkio"; private static final String MEMORY_CTRL = "memory"; + private static final String PIDS_CTRL = "pids"; /* * From https://www.kernel.org/doc/Documentation/filesystems/proc.txt @@ -149,6 +150,7 @@ case CPUSET_CTRL: infos.put(CPUSET_CTRL, info); break; case MEMORY_CTRL: infos.put(MEMORY_CTRL, info); break; case BLKIO_CTRL: infos.put(BLKIO_CTRL, info); break; + case PIDS_CTRL: infos.put(PIDS_CTRL, info); break; } } @@ -252,6 +254,7 @@ case CPUACCT_CTRL: case CPU_CTRL: case BLKIO_CTRL: + case PIDS_CTRL: CgroupInfo info = infos.get(cName); info.setCgroupPath(cgroupPath); break; @@ -303,6 +306,7 @@ case MEMORY_CTRL: // fall-through case CPU_CTRL: case CPUACCT_CTRL: + case PIDS_CTRL: case BLKIO_CTRL: { CgroupInfo info = infos.get(controllerName); assert info.getMountPoint() == null; diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ private CgroupV1SubsystemController cpuacct; private CgroupV1SubsystemController cpuset; private CgroupV1SubsystemController blkio; + private CgroupV1SubsystemController pids; private static volatile CgroupV1Subsystem INSTANCE; @@ -126,6 +127,15 @@ } break; } + case "pids": { + if (info.getMountRoot() != null && info.getMountPoint() != null) { + CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint()); + controller.setPath(info.getCgroupPath()); + subsystem.setPidsController(controller); + anyActiveControllers = true; + } + break; + } default: throw new AssertionError("Unrecognized controller in infos: " + info.getName()); } @@ -170,6 +180,10 @@ this.blkio = blkio; } + private void setPidsController(CgroupV1SubsystemController pids) { + this.pids = pids; + } + private static long getLongValue(CgroupSubsystemController controller, String parm) { return CgroupSubsystemController.getLongValue(controller, @@ -394,6 +408,17 @@ return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.soft_limit_in_bytes")); } + /***************************************************************** + * pids subsystem + ****************************************************************/ + public long getPidsMax() { + String pidsMaxStr = CgroupSubsystemController.getStringValue(pids, "pids.max"); + return CgroupSubsystem.limitFromString(pidsMaxStr); + } + + public long getPidsCurrent() { + return getLongValue(pids, "pids.current"); + } /***************************************************************** * BlKIO Subsystem diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Red Hat Inc. + * Copyright (c) 2020, 2021, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,6 @@ private final CgroupSubsystemController unified; private static final String PROVIDER_NAME = "cgroupv2"; private static final int PER_CPU_SHARES = 1024; - private static final String MAX_VAL = "max"; private static final Object EMPTY_STR = ""; private static final long NO_SWAP = 0; @@ -149,14 +148,7 @@ return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } String quota = tokens[tokenIdx]; - return limitFromString(quota); - } - - private long limitFromString(String strVal) { - if (strVal == null || MAX_VAL.equals(strVal)) { - return CgroupSubsystem.LONG_RETVAL_UNLIMITED; - } - return Long.parseLong(strVal); + return CgroupSubsystem.limitFromString(quota); } @Override @@ -251,7 +243,7 @@ @Override public long getMemoryLimit() { String strVal = CgroupSubsystemController.getStringValue(unified, "memory.max"); - return limitFromString(strVal); + return CgroupSubsystem.limitFromString(strVal); } @Override @@ -279,7 +271,7 @@ if (strVal == null) { return getMemoryLimit(); } - long swapLimit = limitFromString(strVal); + long swapLimit = CgroupSubsystem.limitFromString(strVal); if (swapLimit >= 0) { long memoryLimit = getMemoryLimit(); assert memoryLimit >= 0; @@ -310,7 +302,18 @@ @Override public long getMemorySoftLimit() { String softLimitStr = CgroupSubsystemController.getStringValue(unified, "memory.low"); - return limitFromString(softLimitStr); + return CgroupSubsystem.limitFromString(softLimitStr); + } + + @Override + public long getPidsMax() { + String pidsMaxStr = CgroupSubsystemController.getStringValue(unified, "pids.max"); + return CgroupSubsystem.limitFromString(pidsMaxStr); + } + + @Override + public long getPidsCurrent() { + return getLongVal("pids.current"); } @Override diff -Nru openjdk-17-17.0.2+8/src/java.base/macosx/classes/apple/security/KeychainStore.java openjdk-17-17.0.3+7/src/java.base/macosx/classes/apple/security/KeychainStore.java --- openjdk-17-17.0.2+8/src/java.base/macosx/classes/apple/security/KeychainStore.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/macosx/classes/apple/security/KeychainStore.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,6 +68,25 @@ Certificate cert; long certRef; // SecCertificateRef for this key + + // Each KeyStore.TrustedCertificateEntry have 2 attributes: + // 1. "trustSettings" -> trustSettings.toString() + // 2. "2.16.840.1.113894.746875.1.1" -> trustedKeyUsageValue + // The 1st one is mainly for debugging use. The 2nd one is similar + // to the attribute with the same key in a PKCS12KeyStore. + + // The SecTrustSettingsCopyTrustSettings() output for this certificate + // inside the KeyChain in its original array of CFDictionaryRef objects + // structure with values dumped as strings. For each trust, an extra + // entry "SecPolicyOid" is added whose value is the OID for this trust. + // The extra entries are used to construct trustedKeyUsageValue. + List> trustSettings; + + // One or more OIDs defined in http://oidref.com/1.2.840.113635.100.1. + // It can also be "2.5.29.37.0" for a self-signed certificate with + // an empty trust settings. This value is never empty. When there are + // multiple OID values, it takes the form of "[1.1.1, 1.1.2]". + String trustedKeyUsageValue; }; /** @@ -300,6 +319,35 @@ } } + private record LocalAttr(String name, String value) + implements KeyStore.Entry.Attribute { + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + } + + @Override + public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter protParam) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { + if (engineIsCertificateEntry(alias)) { + Object entry = entries.get(alias.toLowerCase()); + if (entry instanceof TrustedCertEntry tEntry) { + return new KeyStore.TrustedCertificateEntry( + tEntry.cert, Set.of( + new LocalAttr(KnownOIDs.ORACLE_TrustedKeyUsage.value(), tEntry.trustedKeyUsageValue), + new LocalAttr("trustSettings", tEntry.trustSettings.toString()))); + } + } + return super.engineGetEntry(alias, protParam); + } + /** * Returns the creation date of the entry identified by the given alias. * @@ -453,55 +501,12 @@ } /** - * Assigns the given certificate to the given alias. - * - *

    If the given alias already exists in this keystore and identifies a - * trusted certificate entry, the certificate associated with it is - * overridden by the given certificate. - * - * @param alias the alias name - * @param cert the certificate - * - * @exception KeyStoreException if the given alias already exists and does - * not identify a trusted certificate entry, or this operation - * fails for some other reason. + * Adding trusted certificate entry is not supported. */ public void engineSetCertificateEntry(String alias, Certificate cert) - throws KeyStoreException - { - permissionCheck(); - - synchronized(entries) { - - Object entry = entries.get(alias.toLowerCase()); - if ((entry != null) && (entry instanceof KeyEntry)) { - throw new KeyStoreException - ("Cannot overwrite key entry with certificate"); - } - - // This will be slow, but necessary. Enumerate the values and then see if the cert matches the one in the trusted cert entry. - // Security framework doesn't support the same certificate twice in a keychain. - Collection allValues = entries.values(); - - for (Object value : allValues) { - if (value instanceof TrustedCertEntry) { - TrustedCertEntry tce = (TrustedCertEntry)value; - if (tce.cert.equals(cert)) { - throw new KeyStoreException("Keychain does not support mulitple copies of same certificate."); - } - } - } - - TrustedCertEntry trustedCertEntry = new TrustedCertEntry(); - trustedCertEntry.cert = cert; - trustedCertEntry.date = new Date(); - String lowerAlias = alias.toLowerCase(); - if (entries.get(lowerAlias) != null) { - deletedEntries.put(lowerAlias, entries.get(lowerAlias)); - } - entries.put(lowerAlias, trustedCertEntry); - addedEntries.put(lowerAlias, trustedCertEntry); - } + throws KeyStoreException { + throw new KeyStoreException("Cannot set trusted certificate entry." + + " Use the macOS \"security add-trusted-cert\" command instead."); } /** @@ -680,10 +685,7 @@ String alias = e.nextElement(); Object entry = addedEntries.get(alias); if (entry instanceof TrustedCertEntry) { - TrustedCertEntry tce = (TrustedCertEntry)entry; - Certificate certElem; - certElem = tce.cert; - tce.certRef = addCertificateToKeychain(alias, certElem); + // Cannot set trusted certificate entry } else { KeyEntry keyEntry = (KeyEntry)entry; @@ -778,9 +780,28 @@ private native void _scanKeychain(); /** - * Callback method from _scanKeychain. If a trusted certificate is found, this method will be called. + * Callback method from _scanKeychain. If a trusted certificate is found, + * this method will be called. + * + * inputTrust is a list of strings in groups. Each group contains key/value + * pairs for one trust setting and ends with a null. Thus the size of the + * whole list is (2 * s_1 + 1) + (2 * s_2 + 1) + ... + (2 * s_n + 1), + * where s_i is the size of mapping for the i'th trust setting, + * and n is the number of trust settings. Ex: + * + * key1 for trust1 + * value1 for trust1 + * .. + * null (end of trust1) + * key1 for trust2 + * value1 for trust2 + * ... + * null (end of trust2) + * ... + * null (end if trust_n) */ - private void createTrustedCertEntry(String alias, long keychainItemRef, long creationDate, byte[] derStream) { + private void createTrustedCertEntry(String alias, List inputTrust, + long keychainItemRef, long creationDate, byte[] derStream) { TrustedCertEntry tce = new TrustedCertEntry(); try { @@ -791,6 +812,69 @@ tce.cert = cert; tce.certRef = keychainItemRef; + tce.trustSettings = new ArrayList<>(); + Map tmpMap = new LinkedHashMap<>(); + for (int i = 0; i < inputTrust.size(); i++) { + if (inputTrust.get(i) == null) { + tce.trustSettings.add(tmpMap); + if (i < inputTrust.size() - 1) { + // Prepare an empty map for the next trust setting. + // Do not just clear(), must be a new object. + // Only create if not at end of list. + tmpMap = new LinkedHashMap<>(); + } + } else { + tmpMap.put(inputTrust.get(i), inputTrust.get(i+1)); + i++; + } + } + + boolean isSelfSigned; + try { + cert.verify(cert.getPublicKey()); + isSelfSigned = true; + } catch (Exception e) { + isSelfSigned = false; + } + if (tce.trustSettings.isEmpty()) { + if (isSelfSigned) { + // If a self-signed certificate has an empty trust settings, + // trust it for all purposes + tce.trustedKeyUsageValue = KnownOIDs.anyExtendedKeyUsage.value(); + } else { + // Otherwise, return immediately. The certificate is not + // added into entries. + return; + } + } else { + List values = new ArrayList<>(); + for (var oneTrust : tce.trustSettings) { + var result = oneTrust.get("kSecTrustSettingsResult"); + // https://developer.apple.com/documentation/security/sectrustsettingsresult?language=objc + // 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot + // If missing, a default value of kSecTrustSettingsResultTrustRoot is assumed + // for self-signed certificates (see doc for SecTrustSettingsCopyTrustSettings). + // Note that the same SecPolicyOid can appear in multiple trust settings + // for different kSecTrustSettingsAllowedError and/or kSecTrustSettingsPolicyString. + if ((result == null && isSelfSigned) + || "1".equals(result) || "2".equals(result)) { + // When no kSecTrustSettingsPolicy, it means everything + String oid = oneTrust.getOrDefault("SecPolicyOid", + KnownOIDs.anyExtendedKeyUsage.value()); + if (!values.contains(oid)) { + values.add(oid); + } + } + } + if (values.isEmpty()) { + return; + } + if (values.size() == 1) { + tce.trustedKeyUsageValue = values.get(0); + } else { + tce.trustedKeyUsageValue = values.toString(); + } + } // Make a creation date. if (creationDate != 0) tce.date = new Date(creationDate); diff -Nru openjdk-17-17.0.2+8/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m openjdk-17-17.0.3+7/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m --- openjdk-17-17.0.2+8/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -368,6 +368,14 @@ } } +#define ADD(list, str) { \ + jobject localeObj = (*env)->NewStringUTF(env, [str UTF8String]); \ + (*env)->CallBooleanMethod(env, list, jm_listAdd, localeObj); \ + (*env)->DeleteLocalRef(env, localeObj); \ +} + +#define ADDNULL(list) (*env)->CallBooleanMethod(env, list, jm_listAdd, NULL) + static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) { // Search the user keychain list for all X509 certificates. @@ -379,8 +387,15 @@ jclass jc_KeychainStore = (*env)->FindClass(env, "apple/security/KeychainStore"); CHECK_NULL(jc_KeychainStore); jmethodID jm_createTrustedCertEntry = (*env)->GetMethodID( - env, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;JJ[B)V"); + env, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;Ljava/util/List;JJ[B)V"); CHECK_NULL(jm_createTrustedCertEntry); + jclass jc_arrayListClass = (*env)->FindClass(env, "java/util/ArrayList"); + CHECK_NULL(jc_arrayListClass); + jmethodID jm_arrayListCons = (*env)->GetMethodID(env, jc_arrayListClass, "", "()V"); + CHECK_NULL(jm_arrayListCons); + jmethodID jm_listAdd = (*env)->GetMethodID(env, jc_arrayListClass, "add", "(Ljava/lang/Object;)Z"); + CHECK_NULL(jm_listAdd); + do { searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem); @@ -401,12 +416,50 @@ goto errOut; } + // Only add certificates with trusted settings + CFArrayRef trustSettings; + if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings) + == errSecItemNotFound) { + continue; + } + + // See KeychainStore::createTrustedCertEntry for content of inputTrust + jobject inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); + CHECK_NULL(inputTrust); + + // Dump everything inside trustSettings into inputTrust + CFIndex count = CFArrayGetCount(trustSettings); + for (int i = 0; i < count; i++) { + CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i); + CFIndex size = CFDictionaryGetCount(oneTrust); + const void * keys [size]; + const void * values [size]; + CFDictionaryGetKeysAndValues(oneTrust, keys, values); + for (int j = 0; j < size; j++) { + NSString* s = [NSString stringWithFormat:@"%@", keys[j]]; + ADD(inputTrust, s); + s = [NSString stringWithFormat:@"%@", values[j]]; + ADD(inputTrust, s); + } + SecPolicyRef certPolicy; + certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy); + if (certPolicy != NULL) { + CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy); + ADD(inputTrust, @"SecPolicyOid"); + NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")]; + ADD(inputTrust, s); + CFRelease(policyDict); + } + ADDNULL(inputTrust); + } + CFRelease(trustSettings); + // Find the creation date. jlong creationDate = getModDateFromItem(env, theItem); // Call back to the Java object to create Java objects corresponding to this security object. jlong nativeRef = ptr_to_jlong(certRef); - (*env)->CallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, nativeRef, creationDate, certData); + (*env)->CallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, inputTrust, nativeRef, creationDate, certData); JNU_CHECK_EXCEPTION(env); } } while (searchResult == noErr); @@ -522,8 +575,8 @@ /* * Class: apple_security_KeychainStore * Method: _addItemToKeychain - * Signature: (Ljava/lang/String;[B)I -*/ + * Signature: (Ljava/lang/String;Z[B[C)J + */ JNIEXPORT jlong JNICALL Java_apple_security_KeychainStore__1addItemToKeychain (JNIEnv *env, jobject this, jstring alias, jboolean isCertificate, jbyteArray rawDataObj, jcharArray passwordObj) { diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/io/File.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/io/File.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/io/File.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/io/File.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -175,8 +175,9 @@ /** * Check if the file has an invalid path. Currently, the inspection of - * a file path is very limited, and it only covers Nul character check. - * Returning true means the path is definitely invalid/garbage. But + * a file path is very limited, and it only covers Nul character check + * unless further checking is explicitly enabled by a system property. + * Returning true means the path is definitely invalid/garbage, but * returning false does not guarantee that the path is valid. * * @return true if the file path is invalid. @@ -184,8 +185,7 @@ final boolean isInvalid() { PathStatus s = status; if (s == null) { - s = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED - : PathStatus.INVALID; + s = fs.isInvalid(this) ? PathStatus.INVALID : PathStatus.CHECKED; status = s; } return s == PathStatus.INVALID; diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/io/FileSystem.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/io/FileSystem.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/io/FileSystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/io/FileSystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -88,6 +88,11 @@ public abstract boolean isAbsolute(File f); /** + * Tell whether the given abstract pathname is invalid. + */ + public abstract boolean isInvalid(File f); + + /** * Resolve the given abstract pathname into absolute form. Invoked by the * getAbsolutePath and getCanonicalPath methods in the File class. */ diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/lang/String.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/lang/String.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/lang/String.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/lang/String.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1284,14 +1284,17 @@ int sp = 0; int sl = val.length >> 1; byte[] dst = new byte[sl * 3]; - char c; - while (sp < sl && (c = StringUTF16.getChar(val, sp)) < '\u0080') { + while (sp < sl) { // ascii fast loop; + char c = StringUTF16.getChar(val, sp); + if (c >= '\u0080') { + break; + } dst[dp++] = (byte)c; sp++; } while (sp < sl) { - c = StringUTF16.getChar(val, sp++); + char c = StringUTF16.getChar(val, sp++); if (c < 0x80) { dst[dp++] = (byte)c; } else if (c < 0x800) { diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/math/BigInteger.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/math/BigInteger.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/math/BigInteger.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/math/BigInteger.java 2022-04-19 19:55:43.000000000 +0000 @@ -1670,8 +1670,8 @@ // are only considering the magnitudes as non-negative. The // Toom-Cook multiplication algorithm determines the sign // at its end from the two signum values. - if (bitLength(mag, mag.length) + - bitLength(val.mag, val.mag.length) > + if ((long)bitLength(mag, mag.length) + + (long)bitLength(val.mag, val.mag.length) > 32L*MAX_MAG_LENGTH) { reportOverflow(); } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/util/random/package-info.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/util/random/package-info.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/util/random/package-info.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/util/random/package-info.java 2022-04-19 19:55:43.000000000 +0000 @@ -599,32 +599,32 @@ * {@code 0xd1342543de82ef95L} * {@code xoroshiro128}, version 1.0 * {@code (24, 16, 37)} - * mixLea32{@code (s+x0)} + * mixLea64{@code (s+x0)} * "L64X256MixRandom" * {@code 0xd1342543de82ef95L} * {@code xoshiro256}, version 1.0 * {@code (17, 45)} - * mixLea32{@code (s+x0)} + * mixLea64{@code (s+x0)} * "L64X1024MixRandom" * {@code 0xd1342543de82ef95L} * {@code xoroshiro1024}, version 1.0 * {@code (25, 27, 36)} - * mixLea32{@code (s+x0)} + * mixLea64{@code (s+x0)} * "L128X128MixRandom" * {@code 0x1d605bbb58c8abbfdL} * {@code xoroshiro128}, version 1.0 * {@code (24, 16, 37)} - * mixLea32{@code (sh+x0)} + * mixLea64{@code (sh+x0)} * "L128X256MixRandom" * {@code 0x1d605bbb58c8abbfdL} * {@code xoshiro256}, version 1.0 * {@code (17, 45)} - * mixLea32{@code (sh+x0)} + * mixLea64{@code (sh+x0)} * "L128X1024MixRandom" * {@code 0x1d605bbb58c8abbfdL} * {@code xoroshiro1024}, version 1.0 * {@code (25, 27, 36)} - * mixLea32{@code (sh+x0)} + * mixLea64{@code (sh+x0)} * * * diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java openjdk-17-17.0.3+7/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -83,12 +83,12 @@ "must be non-null"); } - DerValue val = new DerValue(encoded); + this.encoded = encoded.clone(); + DerValue val = DerValue.wrap(this.encoded); if (val.tag != DerValue.tag_Sequence) { throw new IOException("DER header error: no SEQ tag"); } - this.encoded = encoded.clone(); DerValue[] seq = new DerValue[2]; seq[0] = val.data.getDerValue(); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java 2022-04-19 19:55:43.000000000 +0000 @@ -309,7 +309,7 @@ return -1; } } - return length; + return current - offset + length; } static int mutf8FromStringLength(String s) { diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/platform/Metrics.java openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/platform/Metrics.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/platform/Metrics.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/platform/Metrics.java 2022-04-19 19:55:43.000000000 +0000 @@ -353,6 +353,27 @@ public long getMemorySoftLimit(); /***************************************************************** + * pids subsystem + ****************************************************************/ + + /** + * Returns the maximum number of tasks that may be created in the Isolation Group. + * + * @return The maximum number of tasks, -1 if the quota is unlimited or + * -2 if not supported. + * + */ + public long getPidsMax(); + + /** + * Returns the current number of tasks in the Isolation Group. + * + * @return The current number of tasks or -2 if not supported + * + */ + public long getPidsCurrent(); + + /***************************************************************** * BlKIO Subsystem ****************************************************************/ diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java 2022-04-19 19:55:43.000000000 +0000 @@ -2380,7 +2380,7 @@ long bits = nextLong(); long multiplier = (1L << SALT_SHIFT) - 1; long salt = multiplier << (64 - SALT_SHIFT); - while ((salt & multiplier) != 0) { + while ((salt & multiplier) == 0) { long digit = Math.multiplyHigh(bits, multiplier); salt = (salt >>> SALT_SHIFT) | (digit << (64 - SALT_SHIFT)); bits *= multiplier; diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/launcher/LauncherHelper.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/launcher/LauncherHelper.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -405,12 +405,23 @@ limit = c.getMemoryAndSwapLimit(); ostream.println(formatLimitString(limit, INDENT + "Memory & Swap Limit: ", longRetvalNotSupported)); + limit = c.getPidsMax(); + ostream.println(formatLimitString(limit, INDENT + "Maximum Processes Limit: ", + longRetvalNotSupported, false)); ostream.println(""); } private static String formatLimitString(long limit, String prefix, long unavailable) { + return formatLimitString(limit, prefix, unavailable, true); + } + + private static String formatLimitString(long limit, String prefix, long unavailable, boolean scale) { if (limit >= 0) { - return prefix + SizePrefix.scaleValue(limit); + if (scale) { + return prefix + SizePrefix.scaleValue(limit); + } else { + return prefix + limit; + } } else if (limit == unavailable) { return prefix + "N/A"; } else { diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java 2022-04-19 19:55:43.000000000 +0000 @@ -658,8 +658,8 @@ if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) { value = new AnnotationTypeMismatchExceptionProxy( - value.getClass() + "[" + value + "]").setMember( - annotationType.members().get(name)); + objectToString(value)) + .setMember(annotationType.members().get(name)); } } mv.put(name, value); @@ -669,6 +669,15 @@ UnsafeAccessor.setMemberValues(this, mv); } + /* + * Create a textual representation of the argument without calling + * any overridable methods of the argument. + */ + private static String objectToString(Object value) { + return value.getClass().getName() + "@" + + Integer.toHexString(System.identityHashCode(value)); + } + private static class UnsafeAccessor { private static final jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/DSA.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/DSA.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/DSA.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/DSA.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -362,7 +362,8 @@ s = new BigInteger(1, s.toByteArray()); } - if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1)) { + if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1) + && r.signum() > 0 && s.signum() > 0) { BigInteger w = generateW(presetP, presetQ, presetG, s); BigInteger v = generateV(presetY, presetP, presetQ, presetG, w, r); return v.equals(r); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -226,22 +226,26 @@ List extensions) throws IOException { OCSPRequest request = new OCSPRequest(certIds, extensions); byte[] bytes = request.encodeBytes(); + String responder = responderURI.toString(); if (debug != null) { - debug.println("connecting to OCSP service at: " + responderURI); + debug.println("connecting to OCSP service at: " + responder); } Event.report(Event.ReporterCategory.CRLCHECK, "event.ocsp.check", - responderURI.toString()); + responder); URL url; HttpURLConnection con = null; try { - String encodedGetReq = responderURI.toString() + "/" + - URLEncoder.encode(Base64.getEncoder().encodeToString(bytes), - "UTF-8"); + StringBuilder encodedGetReq = new StringBuilder(responder); + if (!responder.endsWith("/")) { + encodedGetReq.append("/"); + } + encodedGetReq.append(URLEncoder.encode( + Base64.getEncoder().encodeToString(bytes), "UTF-8")); if (encodedGetReq.length() <= 255) { - url = new URL(encodedGetReq); + url = new URL(encodedGetReq.toString()); con = (HttpURLConnection)url.openConnection(); con.setDoOutput(true); con.setDoInput(true); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java 2022-04-19 19:55:43.000000000 +0000 @@ -638,7 +638,10 @@ try { Signature respSignature = Signature.getInstance(sigAlgId.getName()); - respSignature.initVerify(cert.getPublicKey()); + SignatureUtil.initVerifyWithParam(respSignature, + cert.getPublicKey(), + SignatureUtil.getParamSpec(sigAlgId.getName(), + sigAlgId.getEncodedParams())); respSignature.update(tbsResponseData); if (respSignature.verify(signature)) { @@ -654,8 +657,8 @@ } return false; } - } catch (InvalidKeyException | NoSuchAlgorithmException | - SignatureException e) + } catch (InvalidAlgorithmParameterException | InvalidKeyException + | NoSuchAlgorithmException | SignatureException e) { throw new CertPathValidatorException(e); } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java 2022-04-19 19:55:43.000000000 +0000 @@ -123,12 +123,14 @@ @Override protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { - if (!(publicKey instanceof RSAPublicKey)) { + if (publicKey instanceof RSAPublicKey rsaPubKey) { + isPublicKeyValid(rsaPubKey); + this.pubKey = rsaPubKey; + this.privKey = null; + resetDigest(); + } else { throw new InvalidKeyException("key must be RSAPublicKey"); } - this.pubKey = (RSAPublicKey) isValid((RSAKey)publicKey); - this.privKey = null; - resetDigest(); } // initialize for signing. See JCA doc @@ -142,14 +144,16 @@ @Override protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { - if (!(privateKey instanceof RSAPrivateKey)) { + if (privateKey instanceof RSAPrivateKey rsaPrivateKey) { + isPrivateKeyValid(rsaPrivateKey); + this.privKey = rsaPrivateKey; + this.pubKey = null; + this.random = + (random == null ? JCAUtil.getSecureRandom() : random); + resetDigest(); + } else { throw new InvalidKeyException("key must be RSAPrivateKey"); } - this.privKey = (RSAPrivateKey) isValid((RSAKey)privateKey); - this.pubKey = null; - this.random = - (random == null? JCAUtil.getSecureRandom() : random); - resetDigest(); } /** @@ -202,10 +206,55 @@ } /** + * Validate the specified RSAPrivateKey + */ + private void isPrivateKeyValid(RSAPrivateKey prKey) throws InvalidKeyException { + try { + if (prKey instanceof RSAPrivateCrtKey crtKey) { + if (RSAPrivateCrtKeyImpl.checkComponents(crtKey)) { + RSAKeyFactory.checkRSAProviderKeyLengths( + crtKey.getModulus().bitLength(), + crtKey.getPublicExponent()); + } else { + throw new InvalidKeyException( + "Some of the CRT-specific components are not available"); + } + } else { + RSAKeyFactory.checkRSAProviderKeyLengths( + prKey.getModulus().bitLength(), + null); + } + } catch (InvalidKeyException ikEx) { + throw ikEx; + } catch (Exception e) { + throw new InvalidKeyException( + "Can not access private key components", e); + } + isValid(prKey); + } + + /** + * Validate the specified RSAPublicKey + */ + private void isPublicKeyValid(RSAPublicKey pKey) throws InvalidKeyException { + try { + RSAKeyFactory.checkRSAProviderKeyLengths( + pKey.getModulus().bitLength(), + pKey.getPublicExponent()); + } catch (InvalidKeyException ikEx) { + throw ikEx; + } catch (Exception e) { + throw new InvalidKeyException( + "Can not access public key components", e); + } + isValid(pKey); + } + + /** * Validate the specified RSAKey and its associated parameters against * internal signature parameters. */ - private RSAKey isValid(RSAKey rsaKey) throws InvalidKeyException { + private void isValid(RSAKey rsaKey) throws InvalidKeyException { AlgorithmParameterSpec keyParams = rsaKey.getParams(); // validate key parameters if (!isCompatible(rsaKey.getParams(), this.sigParams)) { @@ -232,7 +281,6 @@ ("Unrecognized digest algo: " + digestAlgo); } } - return rsaKey; } /** diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -91,16 +91,11 @@ RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo); // check all CRT-specific components are available, if any one // missing, return a non-CRT key instead - if ((key.getPublicExponent().signum() == 0) || - (key.getPrimeExponentP().signum() == 0) || - (key.getPrimeExponentQ().signum() == 0) || - (key.getPrimeP().signum() == 0) || - (key.getPrimeQ().signum() == 0) || - (key.getCrtCoefficient().signum() == 0)) { + if (checkComponents(key)) { + return key; + } else { return new RSAPrivateKeyImpl(key.type, key.keyParams, key.getModulus(), key.getPrivateExponent()); - } else { - return key; } case "PKCS#1": try { @@ -125,6 +120,18 @@ } /** + * Validate if all CRT-specific components are available. + */ + static boolean checkComponents(RSAPrivateCrtKey key) { + return !((key.getPublicExponent().signum() == 0) || + (key.getPrimeExponentP().signum() == 0) || + (key.getPrimeExponentQ().signum() == 0) || + (key.getPrimeP().signum() == 0) || + (key.getPrimeQ().signum() == 0) || + (key.getCrtCoefficient().signum() == 0)); + } + + /** * Generate a new key from the specified type and components. * Returns a CRT key if possible and a non-CRT key otherwise. * Used by SunPKCS11 provider. diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -107,6 +107,11 @@ private static final boolean trustNameService = Utilities.getBooleanProperty("jdk.tls.trustNameService", false); + /* + * Default timeout to skip bytes from the open socket + */ + private static final int DEFAULT_SKIP_TIMEOUT = 1; + /** * Package-private constructor used to instantiate an unconnected * socket. @@ -1781,9 +1786,21 @@ if (conContext.inputRecord instanceof SSLSocketInputRecord inputRecord && isConnected) { if (appInput.readLock.tryLock()) { + int soTimeout = getSoTimeout(); try { + // deplete could hang on the skip operation + // in case of infinite socket read timeout. + // Change read timeout to avoid deadlock. + // This workaround could be replaced later + // with the right synchronization + if (soTimeout == 0) + setSoTimeout(DEFAULT_SKIP_TIMEOUT); inputRecord.deplete(false); + } catch (java.net.SocketTimeoutException stEx) { + // skip timeout exception during deplete } finally { + if (soTimeout == 0) + setSoTimeout(soTimeout); appInput.readLock.unlock(); } } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/tools/keytool/Main.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/tools/keytool/Main.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/tools/keytool/Main.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/tools/keytool/Main.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2208,6 +2208,9 @@ out.println(mf); dumpCert(cert, out); } else if (debug) { + for (var attr : keyStore.getEntry(alias, null).getAttributes()) { + System.out.println("Attribute " + attr.getName() + ": " + attr.getValue()); + } out.println(cert.toString()); } else { out.println("trustedCertEntry, "); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java 2022-04-19 19:55:43.000000000 +0000 @@ -144,15 +144,14 @@ * then skip the tag and its 1 byte length of zero. */ private void writeTag() { - if (dataPos == dataSize) { - return; - } - assert dataPos + 1 < dataSize; - if (isEOC(data, dataPos)) { - dataPos += 2; // skip tag and length - writeTag(); - } else { - newData[newDataPos++] = data[dataPos++]; + while (dataPos < dataSize) { + assert dataPos + 1 < dataSize; + if (isEOC(data, dataPos)) { + dataPos += 2; // skip tag and length + } else { + newData[newDataPos++] = data[dataPos++]; + break; + } } } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/DerValue.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/DerValue.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/DerValue.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/DerValue.java 2022-04-19 19:55:43.000000000 +0000 @@ -303,6 +303,34 @@ } /** + * Wraps a byte array as a single DerValue. + * + * Attention: no cloning is made. + * + * @param buf the byte array containing the DER-encoded datum + * @returns a new DerValue + */ + public static DerValue wrap(byte[] buf) + throws IOException { + return wrap(buf, 0, buf.length); + } + + /** + * Wraps a byte array as a single DerValue. + * + * Attention: no cloning is made. + * + * @param buf the byte array containing the DER-encoded datum + * @param offset where the encoded datum starts inside {@code buf} + * @param len length of bytes to parse inside {@code buf} + * @returns a new DerValue + */ + public static DerValue wrap(byte[] buf, int offset, int len) + throws IOException { + return new DerValue(buf, offset, len, true, false); + } + + /** * Parse an ASN.1/BER encoded datum. The entire encoding must hold exactly * one datum, including its tag and length. * diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -365,7 +365,7 @@ if ((encoding[i] & 0x80) == 0) { // one section [fromPos..i] if (i - fromPos + 1 > 4) { - BigInteger big = new BigInteger(pack(encoding, + BigInteger big = new BigInteger(1, pack(encoding, fromPos, i-fromPos+1, 7, 8)); if (fromPos == 0) { result[which++] = 2; @@ -434,7 +434,7 @@ sb.append('.'); } if (i - fromPos + 1 > 4) { // maybe big integer - BigInteger big = new BigInteger( + BigInteger big = new BigInteger(1, pack(encoding, fromPos, i-fromPos+1, 7, 8)); if (fromPos == 0) { // first section encoded with more than 4 bytes, @@ -684,6 +684,10 @@ } private static void checkOidSize(int oidLength) throws IOException { + if (oidLength < 0) { + throw new IOException("ObjectIdentifier encoded length was " + + "negative: " + oidLength); + } if (oidLength > MAXIMUM_OID_SIZE) { throw new IOException( "ObjectIdentifier encoded length exceeds " + diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/SignatureUtil.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/SignatureUtil.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/SignatureUtil.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/SignatureUtil.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -170,8 +170,7 @@ // for verification with the specified key and params (may be null) public static void initVerifyWithParam(Signature s, PublicKey key, AlgorithmParameterSpec params) - throws ProviderException, InvalidAlgorithmParameterException, - InvalidKeyException { + throws InvalidAlgorithmParameterException, InvalidKeyException { SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, key, params); } @@ -180,8 +179,7 @@ public static void initVerifyWithParam(Signature s, java.security.cert.Certificate cert, AlgorithmParameterSpec params) - throws ProviderException, InvalidAlgorithmParameterException, - InvalidKeyException { + throws InvalidAlgorithmParameterException, InvalidKeyException { SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, cert, params); } @@ -189,8 +187,7 @@ // for signing with the specified key and params (may be null) public static void initSignWithParam(Signature s, PrivateKey key, AlgorithmParameterSpec params, SecureRandom sr) - throws ProviderException, InvalidAlgorithmParameterException, - InvalidKeyException { + throws InvalidAlgorithmParameterException, InvalidKeyException { SharedSecrets.getJavaSecuritySignatureAccess().initSign(s, key, params, sr); } @@ -342,10 +339,10 @@ * Create a Signature that has been initialized with proper key and params. * * @param sigAlg signature algorithms - * @param key public or private key + * @param key private key * @param provider (optional) provider */ - public static Signature fromKey(String sigAlg, Key key, String provider) + public static Signature fromKey(String sigAlg, PrivateKey key, String provider) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException{ Signature sigEngine = (provider == null || provider.isEmpty()) @@ -358,10 +355,10 @@ * Create a Signature that has been initialized with proper key and params. * * @param sigAlg signature algorithms - * @param key public or private key + * @param key private key * @param provider (optional) provider */ - public static Signature fromKey(String sigAlg, Key key, Provider provider) + public static Signature fromKey(String sigAlg, PrivateKey key, Provider provider) throws NoSuchAlgorithmException, InvalidKeyException{ Signature sigEngine = (provider == null) ? Signature.getInstance(sigAlg) @@ -369,17 +366,12 @@ return autoInitInternal(sigAlg, key, sigEngine); } - private static Signature autoInitInternal(String alg, Key key, Signature s) + private static Signature autoInitInternal(String alg, PrivateKey key, Signature s) throws InvalidKeyException { AlgorithmParameterSpec params = SignatureUtil .getDefaultParamSpec(alg, key); try { - if (key instanceof PrivateKey) { - SignatureUtil.initSignWithParam(s, (PrivateKey) key, params, - null); - } else { - SignatureUtil.initVerifyWithParam(s, (PublicKey) key, params); - } + SignatureUtil.initSignWithParam(s, key, params, null); } catch (InvalidAlgorithmParameterException e) { throw new AssertionError("Should not happen", e); } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/AlgorithmId.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/AlgorithmId.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/AlgorithmId.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/AlgorithmId.java 2022-04-19 19:55:43.000000000 +0000 @@ -312,7 +312,7 @@ * * @return DER encoded parameters, or null not present. */ - public byte[] getEncodedParams() throws IOException { + public byte[] getEncodedParams() { return (encodedParams == null || algid.toString().equals(KnownOIDs.SpecifiedSHA2withECDSA.value())) ? null diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -820,13 +820,7 @@ * null if no parameters are present. */ public byte[] getSigAlgParams() { - if (sigAlgId == null) - return null; - try { - return sigAlgId.getEncodedParams(); - } catch (IOException e) { - return null; - } + return sigAlgId == null ? null : sigAlgId.getEncodedParams(); } /** diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/X509CertImpl.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/X509CertImpl.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/X509CertImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/X509CertImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -1030,13 +1030,7 @@ * null if no parameters are present. */ public byte[] getSigAlgParams() { - if (algId == null) - return null; - try { - return algId.getEncodedParams(); - } catch (IOException e) { - return null; - } + return algId == null ? null : algId.getEncodedParams(); } /** diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties openjdk-17-17.0.3+7/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -251,6 +251,7 @@ UYU=UYU UZS=UZS VEB=VEB +VED=VED VEF=VEF VES=VES VND=VND @@ -474,6 +475,7 @@ uyu=Uruguayan Peso uzs=Uzbekistan Som veb=Venezuelan Bol\u00edvar (1871-2008) +ved=Venezuelan Bol\u00edvar Soberano vef=Venezuelan Bol\u00edvar ves=Venezuelan Bol\u00edvar Soberano vnd=Vietnamese Dong diff -Nru openjdk-17-17.0.2+8/src/java.base/unix/classes/java/io/UnixFileSystem.java openjdk-17-17.0.3+7/src/java.base/unix/classes/java/io/UnixFileSystem.java --- openjdk-17-17.0.2+8/src/java.base/unix/classes/java/io/UnixFileSystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/unix/classes/java/io/UnixFileSystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,6 +142,11 @@ } @Override + public boolean isInvalid(File f) { + return f.getPath().indexOf('\u0000') < 0 ? false : true; + } + + @Override public String resolve(File f) { if (isAbsolute(f)) return f.getPath(); @SuppressWarnings("removal") diff -Nru openjdk-17-17.0.2+8/src/java.base/unix/classes/sun/net/www/content-types.properties openjdk-17-17.0.3+7/src/java.base/unix/classes/sun/net/www/content-types.properties --- openjdk-17-17.0.2+8/src/java.base/unix/classes/sun/net/www/content-types.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/unix/classes/sun/net/www/content-types.properties 2022-04-19 19:55:43.000000000 +0000 @@ -27,7 +27,7 @@ # application/octet-stream: \ description=Generic Binary Stream;\ - file_extensions=.saveme,.dump,.hqx,.arc,.o,.a,.bin,.exe,.z,.gz + file_extensions=.saveme,.dump,.hqx,.arc,.o,.a,.bin,.exe,.z application/oda: \ description=ODA Document;\ @@ -253,14 +253,26 @@ description=Bitmap Image;\ file_extensions=.bmp; +image/webp: \ + description=WEBP image;\ + file_extensions=.webp; + +text/css: \ + description=CSS File;\ + file_extensions=.css; + text/html: \ description=HTML Document;\ file_extensions=.htm,.html;\ icon=html +text/javascript: \ + description=JavaScript File;\ + file_extensions=.js; + text/plain: \ description=Plain Text;\ - file_extensions=.text,.c,.cc,.c++,.h,.pl,.txt,.java,.el;\ + file_extensions=.text,.c,.cc,.c++,.h,.pl,.txt,.java,.el,.php,.adoc,.py;\ icon=text;\ action=browser @@ -272,6 +284,14 @@ description=Structure Enhanced Text;\ file_extensions=.etx +text/csv: \ + description=CSV File;\ + file_extensions=.csv; + +text/markdown: \ + description=Markdown File;\ + file_extensions=.md,.markdown + video/mp4: \ description=MPEG-4 Video;\ file_extensions=.m4v,.mp4 @@ -311,3 +331,67 @@ application/xml: \ description=XML document;\ file_extensions=.xml + +application/rtf: \ + description=WordPad Document;\ + file_extensions=.rtf; + +application/gzip: \ + description=GZip File;\ + file_extensions=.gz; + +application/vnd.oasis.opendocument.presentation: \ + description=OpenDocument presentation document;\ + file_extensions=.odp; + +application/vnd.oasis.opendocument.spreadsheet: \ + description=OpenDocument spreadsheet document;\ + file_extensions=.ods; + +application/vnd.oasis.opendocument.text: \ + description=OpenDocument text document;\ + file_extensions=.odt; + +application/vnd.ms-excel: \ + description=Microsoft Excel File;\ + file_extensions=.xls; + +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: \ + description=XLSX File;\ + file_extensions=.xlsx; + +application/vnd.openxmlformats-officedocument.presentationml.presentation: \ + description=PPTX File;\ + file_extensions=.pptx; + +application/vnd.ms-powerpoint: \ + description=Microsoft PowerPoint File;\ + file_extensions=.ppt; + +application/x-7z-compressed: \ + description=7-Zip File;\ + file_extensions=.7z; + +application/msword: \ + description=Microsoft Word File;\ + file_extensions=.doc; + +application/vnd.openxmlformats-officedocument.wordprocessingml.document: \ + description=DOCX File;\ + file_extensions=.docx; + +application/vnd.rar: \ + description=RAR File;\ + file_extensions=.rar; + +application/json: \ + description=JSON File;\ + file_extensions=.json; + +application/bz2: \ + description=BZ2 File;\ + file_extensions=.bz2; + +application/java-archive: \ + description=JAR File;\ + file_extensions=.jar; diff -Nru openjdk-17-17.0.2+8/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c openjdk-17-17.0.3+7/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c --- openjdk-17-17.0.2+8/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c 2022-04-19 19:55:43.000000000 +0000 @@ -317,7 +317,7 @@ /* supports extended attributes */ -#ifdef _SYS_XATTR_H_ +#if defined(_SYS_XATTR_H) || defined(_SYS_XATTR_H_) capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_XATTR; #endif @@ -1330,4 +1330,4 @@ if (res == (size_t)-1) throwUnixException(env, errno); return (jint)res; -} \ No newline at end of file +} diff -Nru openjdk-17-17.0.2+8/src/java.base/windows/classes/java/io/WinNTFileSystem.java openjdk-17-17.0.3+7/src/java.base/windows/classes/java/io/WinNTFileSystem.java --- openjdk-17-17.0.2+8/src/java.base/windows/classes/java/io/WinNTFileSystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/windows/classes/java/io/WinNTFileSystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package java.io; import java.io.File; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.BitSet; import java.util.Locale; @@ -45,6 +46,21 @@ private final char semicolon; private final String userDir; + // Whether to enable alternative data streams (ADS) by suppressing + // checking the path for invalid characters, in particular ":". + // ADS support will be enabled if and only if the property is set and + // is the empty string or is equal, ignoring case, to the string "true". + // By default ADS support is disabled. + private static final boolean ENABLE_ADS; + static { + String enableADS = GetPropertyAction.privilegedGetProperty("jdk.io.File.enableADS"); + if (enableADS != null) { + ENABLE_ADS = "".equals(enableADS) || Boolean.parseBoolean(enableADS); + } else { + ENABLE_ADS = false; + } + } + public WinNTFileSystem() { Properties props = GetPropertyAction.privilegedGetProperties(); slash = props.getProperty("file.separator").charAt(0); @@ -306,6 +322,36 @@ } @Override + public boolean isInvalid(File f) { + if (f.getPath().indexOf('\u0000') >= 0) + return true; + + if (ENABLE_ADS) + return false; + + // Invalid if there is a ":" at a position greater than 1, or if there + // is a ":" at position 1 and the first character is not a letter + String pathname = f.getPath(); + int lastColon = pathname.lastIndexOf(":"); + + // Valid if there is no ":" present or if the last ":" present is + // at index 1 and the first character is a latter + if (lastColon < 0 || + (lastColon == 1 && isLetter(pathname.charAt(0)))) + return false; + + // Invalid if path creation fails + Path path = null; + try { + path = sun.nio.fs.DefaultFileSystemProvider.theFileSystem().getPath(pathname); + return false; + } catch (InvalidPathException ignored) { + } + + return true; + } + + @Override public String resolve(File f) { String path = f.getPath(); int pl = f.getPrefixLength(); diff -Nru openjdk-17-17.0.2+8/src/java.base/windows/classes/sun/net/www/content-types.properties openjdk-17-17.0.3+7/src/java.base/windows/classes/sun/net/www/content-types.properties --- openjdk-17-17.0.2+8/src/java.base/windows/classes/sun/net/www/content-types.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/windows/classes/sun/net/www/content-types.properties 2022-04-19 19:55:43.000000000 +0000 @@ -27,7 +27,7 @@ # application/octet-stream: \ description=Generic Binary Stream;\ - file_extensions=.saveme,.dump,.hqx,.arc,.obj,.lib,.bin,.exe,.zip,.gz + file_extensions=.saveme,.dump,.hqx,.arc,.obj,.lib,.bin,.exe application/oda: \ description=ODA Document;\ @@ -43,11 +43,15 @@ icon=ps application/rtf: \ - description=Wordpad Document;\ + description=WordPad Document;\ file_extensions=.rtf;\ action=application;\ application=wordpad.exe %s +application/gzip: \ + description=GZip File;\ + file_extensions=.gz; + application/x-dvi: \ description=TeX DVI File;\ file_extensions=.dvi @@ -250,14 +254,26 @@ description=Bitmap Image;\ file_extensions=.bmp; +image/webp: \ + description=WEBP image;\ + file_extensions=.webp; + +text/css: \ + description=CSS File;\ + file_extensions=.css; + text/html: \ description=HTML Document;\ file_extensions=.htm,.html;\ icon=html +text/javascript: \ + description=JavaScript File;\ + file_extensions=.js; + text/plain: \ description=Plain Text;\ - file_extensions=.text,.c,.cc,.c++,.h,.pl,.txt,.java,.el;\ + file_extensions=.text,.c,.cc,.c++,.h,.pl,.txt,.java,.el,.php,.adoc,.py;\ icon=text;\ action=browser @@ -269,6 +285,14 @@ description=Structure Enhanced Text;\ file_extensions=.etx +text/csv: \ + description=CSV File;\ + file_extensions=.csv; + +text/markdown: \ + description=Markdown File;\ + file_extensions=.md,.markdown + video/mp4: \ description=MPEG-4 Video;\ file_extensions=.m4v,.mp4 @@ -308,3 +332,59 @@ application/xml: \ description=XML document;\ file_extensions=.xml + +application/vnd.oasis.opendocument.presentation: \ + description=OpenDocument presentation document;\ + file_extensions=.odp; + +application/vnd.oasis.opendocument.spreadsheet: \ + description=OpenDocument spreadsheet document;\ + file_extensions=.ods; + +application/vnd.oasis.opendocument.text: \ + description=OpenDocument text document;\ + file_extensions=.odt; + +application/vnd.ms-excel: \ + description=Microsoft Excel File;\ + file_extensions=.xls; + +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: \ + description=XLSX File;\ + file_extensions=.xlsx; + +application/vnd.openxmlformats-officedocument.presentationml.presentation: \ + description=PPTX File;\ + file_extensions=.pptx; + +application/vnd.ms-powerpoint: \ + description=Microsoft PowerPoint File;\ + file_extensions=.ppt; + +application/x-7z-compressed: \ + description=7-Zip File;\ + file_extensions=.7z; + +application/msword: \ + description=Microsoft Word File;\ + file_extensions=.doc; + +application/vnd.openxmlformats-officedocument.wordprocessingml.document: \ + description=DOCX File;\ + file_extensions=.docx; + +application/vnd.rar: \ + description=RAR File;\ + file_extensions=.rar; + +application/json: \ + description=JSON File;\ + file_extensions=.json; + +application/bz2: \ + description=BZ2 File;\ + file_extensions=.bz2; + +application/java-archive: \ + description=JAR File;\ + file_extensions=.jar; diff -Nru openjdk-17-17.0.2+8/src/java.base/windows/native/libnet/DefaultProxySelector.c openjdk-17-17.0.3+7/src/java.base/windows/native/libnet/DefaultProxySelector.c --- openjdk-17-17.0.2+8/src/java.base/windows/native/libnet/DefaultProxySelector.c 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/windows/native/libnet/DefaultProxySelector.c 2022-04-19 19:55:43.000000000 +0000 @@ -104,7 +104,6 @@ int nr_elems = 0; wchar_t *context = NULL; wchar_t *current_proxy = NULL; - BOOL error = FALSE; /* * The proxy server list contains one or more of the following strings @@ -116,7 +115,6 @@ LPWSTR pport; LPWSTR phost; int portVal = 0; - wchar_t *next_proxy = NULL; list_item *proxy = NULL; wchar_t* pos = NULL; @@ -290,7 +288,6 @@ } if (win_proxy != NULL) { - wchar_t *context = NULL; int defport = 0; int nr_elems = 0; @@ -313,27 +310,28 @@ nr_elems = createProxyList(win_proxy, lpProto, &head); if (nr_elems != 0 && head != NULL) { int index = 0; + list_item *current = head; proxy_array = (*env)->NewObjectArray(env, nr_elems, proxy_class, NULL); if (proxy_array == NULL || (*env)->ExceptionCheck(env)) { goto noproxy; } - while (head != NULL && index < nr_elems) { + while (current != NULL && index < nr_elems) { jstring jhost; jobject isa; jobject proxy; - if (head->host != NULL && proxy_array != NULL) { + if (current->host != NULL && proxy_array != NULL) { /* Let's create the appropriate Proxy object then. */ - if (head->port == 0) { - head->port = defport; + if (current->port == 0) { + current->port = defport; } - jhost = (*env)->NewString(env, head->host, (jsize)wcslen(head->host)); + jhost = (*env)->NewString(env, current->host, (jsize)wcslen(current->host)); if (jhost == NULL || (*env)->ExceptionCheck(env)) { proxy_array = NULL; } isa = (*env)->CallStaticObjectMethod(env, isaddr_class, isaddr_createUnresolvedID, jhost, - head->port); + current->port); if (isa == NULL || (*env)->ExceptionCheck(env)) { proxy_array = NULL; } @@ -347,7 +345,7 @@ } index++; } - head = head->next; + current = current->next; } } } diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java 2022-04-19 19:55:43.000000000 +0000 @@ -67,6 +67,7 @@ import com.sun.imageio.plugins.common.I18N; import com.sun.imageio.plugins.common.ImageUtil; +import com.sun.imageio.plugins.common.ReaderUtil; /** This class is the Java Image IO plugin reader for BMP images. * It may subsample the image, clip the image, select sub-bands, @@ -1542,9 +1543,8 @@ } // Read till we have the whole image - byte[] values = new byte[imSize]; - int bytesRead = 0; - iis.readFully(values, 0, imSize); + byte[] values = ReaderUtil. + staggeredReadByteStream(iis, imSize); // Since data is compressed, decompress it decodeRLE8(imSize, padding, values, bdata); @@ -1726,8 +1726,8 @@ } // Read till we have the whole image - byte[] values = new byte[imSize]; - iis.readFully(values, 0, imSize); + byte[] values = ReaderUtil. + staggeredReadByteStream(iis, imSize); // Decompress the RLE4 compressed data. decodeRLE4(imSize, padding, values, bdata); diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,12 +78,26 @@ public Soundbank getSoundbank(AudioInputStream ais) throws InvalidMidiDataException, IOException { + int MEGABYTE = 1048576; + int DEFAULT_BUFFER_SIZE = 65536; + int MAX_FRAME_SIZE = 1024; try { byte[] buffer; - if (ais.getFrameLength() == -1) { + int frameSize = ais.getFormat().getFrameSize(); + if (frameSize <= 0 || frameSize > MAX_FRAME_SIZE) { + throw new InvalidMidiDataException("Formats with frame size " + + frameSize + " are not supported"); + } + + long totalSize = ais.getFrameLength() * frameSize; + if (totalSize >= Integer.MAX_VALUE - 2) { + throw new InvalidMidiDataException( + "Can not allocate enough memory to read audio data."); + } + + if (ais.getFrameLength() == -1 || totalSize > MEGABYTE) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buff = new byte[1024 - - (1024 % ais.getFormat().getFrameSize())]; + byte[] buff = new byte[DEFAULT_BUFFER_SIZE - (DEFAULT_BUFFER_SIZE % frameSize)]; int ret; while ((ret = ais.read(buff)) != -1) { baos.write(buff, 0, ret); @@ -91,8 +105,7 @@ ais.close(); buffer = baos.toByteArray(); } else { - buffer = new byte[(int) (ais.getFrameLength() - * ais.getFormat().getFrameSize())]; + buffer = new byte[(int) totalSize]; new DataInputStream(ais).readFully(buffer); } ModelByteBufferWavetable osc = new ModelByteBufferWavetable( diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/javax/swing/JLabel.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/javax/swing/JLabel.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/javax/swing/JLabel.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/javax/swing/JLabel.java 2022-04-19 19:55:43.000000000 +0000 @@ -1075,6 +1075,10 @@ * @see AccessibleContext#setAccessibleName */ public String getAccessibleName() { + return getAccessibleNameCheckIcon(getAccessibleNameImpl()); + } + + private String getAccessibleNameImpl() { String name = accessibleName; if (name == null) { @@ -1089,6 +1093,19 @@ return name; } + private String getAccessibleNameCheckIcon(String name) { + if (((name == null) || name.isEmpty()) && + (JLabel.this.getIcon() != null)) { + if (JLabel.this.getIcon() instanceof Accessible) { + AccessibleContext ac = ((Accessible) JLabel.this.getIcon()).getAccessibleContext(); + if (ac != null) { + name = ac.getAccessibleName(); + } + } + } + return name; + } + /** * Get the role of this object. * @@ -1097,6 +1114,11 @@ * @see AccessibleRole */ public AccessibleRole getAccessibleRole() { + String name = getAccessibleNameImpl(); + if (((name == null) || name.isEmpty()) && + (JLabel.this.getIcon() != null)) { + return AccessibleRole.ICON; + } return AccessibleRole.LABEL; } diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java 2022-04-19 19:55:43.000000000 +0000 @@ -148,7 +148,7 @@ } /* methods invoked from LCMSTransform */ - public static native void colorConvert(LCMSTransform trans, + public static native void colorConvert(long trans, LCMSImageLayout src, LCMSImageLayout dest); diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java 2022-04-19 19:55:43.000000000 +0000 @@ -44,18 +44,30 @@ import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; +import java.lang.ref.Reference; import sun.java2d.cmm.ColorTransform; import static sun.java2d.cmm.lcms.LCMSImageLayout.ImageLayoutException; final class LCMSTransform implements ColorTransform { - long ID; - private int inFormatter = 0; - private boolean isInIntPacked = false; - private int outFormatter = 0; - private boolean isOutIntPacked = false; + private final static class NativeTransform { + private long ID; + private int inFormatter; + private boolean isInIntPacked; + private int outFormatter; + private boolean isOutIntPacked; + + private boolean match(LCMSImageLayout in, LCMSImageLayout out) { + return inFormatter == in.pixelType + && isInIntPacked == in.isIntPacked + && outFormatter == out.pixelType + && isOutIntPacked == out.isIntPacked; + } + } + + private volatile NativeTransform transform; ICC_Profile[] profiles; LCMSProfile[] lcmsProfiles; int renderType; @@ -64,8 +76,6 @@ private int numInComponents = -1; private int numOutComponents = -1; - private Object disposerReferent = new Object(); - public LCMSTransform(ICC_Profile profile, int renderType, int transformType) { @@ -122,31 +132,32 @@ return numOutComponents; } - private synchronized void doTransform(LCMSImageLayout in, - LCMSImageLayout out) { - // update native transfrom if needed - if (ID == 0L || - inFormatter != in.pixelType || isInIntPacked != in.isIntPacked || - outFormatter != out.pixelType || isOutIntPacked != out.isIntPacked) - { - - if (ID != 0L) { - // Disposer will destroy forgotten transform - disposerReferent = new Object(); + private void doTransform(LCMSImageLayout in, LCMSImageLayout out) { + NativeTransform tfm = transform; + // update native transform if needed + if (tfm == null || !tfm.match(in, out)) { + synchronized (this) { + tfm = transform; + if (tfm == null || !tfm.match(in, out)) { + tfm = new NativeTransform(); + tfm.inFormatter = in.pixelType; + tfm.isInIntPacked = in.isIntPacked; + + tfm.outFormatter = out.pixelType; + tfm.isOutIntPacked = out.isIntPacked; + + tfm.ID = LCMS.createTransform(lcmsProfiles, renderType, + tfm.inFormatter, + tfm.isInIntPacked, + tfm.outFormatter, + tfm.isOutIntPacked, tfm); + // Disposer will destroy forgotten transform + transform = tfm; + } } - inFormatter = in.pixelType; - isInIntPacked = in.isIntPacked; - - outFormatter = out.pixelType; - isOutIntPacked = out.isIntPacked; - - ID = LCMS.createTransform(lcmsProfiles, renderType, - inFormatter, isInIntPacked, - outFormatter, isOutIntPacked, - disposerReferent); } - - LCMS.colorConvert(this, in, out); + LCMS.colorConvert(tfm.ID, in, out); + Reference.reachabilityFence(tfm); // prevent deallocation of "tfm.ID" } /** diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/LCMS.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/LCMS.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/LCMS.c 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/LCMS.c 2022-04-19 19:55:43.000000000 +0000 @@ -71,7 +71,6 @@ } TagSignature_t, *TagSignature_p; static jfieldID Trans_renderType_fID; -static jfieldID Trans_ID_fID; static jfieldID IL_isIntPacked_fID; static jfieldID IL_dataType_fID; static jfieldID IL_pixelType_fID; @@ -510,9 +509,9 @@ * Signature: (Lsun/java2d/cmm/lcms/LCMSTransform;Lsun/java2d/cmm/lcms/LCMSImageLayout;Lsun/java2d/cmm/lcms/LCMSImageLayout;)V */ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert - (JNIEnv *env, jclass cls, jobject trans, jobject src, jobject dst) + (JNIEnv *env, jclass cls, jlong ID, jobject src, jobject dst) { - cmsHTRANSFORM sTrans = NULL; + cmsHTRANSFORM sTrans = jlong_to_ptr(ID); int srcDType, dstDType; int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset; int width, height, i; @@ -533,8 +532,6 @@ srcAtOnce = (*env)->GetBooleanField(env, src, IL_imageAtOnce_fID); dstAtOnce = (*env)->GetBooleanField(env, dst, IL_imageAtOnce_fID); - sTrans = jlong_to_ptr((*env)->GetLongField (env, trans, Trans_ID_fID)); - if (sTrans == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL"); JNU_ThrowByName(env, "java/awt/color/CMMException", @@ -626,11 +623,6 @@ if (Trans_renderType_fID == NULL) { return; } - Trans_ID_fID = (*env)->GetFieldID (env, Trans, "ID", "J"); - if (Trans_ID_fID == NULL) { - return; - } - IL_isIntPacked_fID = (*env)->GetFieldID (env, IL, "isIntPacked", "Z"); if (IL_isIntPacked_fID == NULL) { return; diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsalpha.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsalpha.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsalpha.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsalpha.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,675 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Alpha copy ------------------------------------------------------------------------------------------------------------------ + +// This macro return words stored as big endian +#define CHANGE_ENDIAN(w) (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8)) + + +// Floor to byte, taking care of saturation +cmsINLINE cmsUInt8Number _cmsQuickSaturateByte(cmsFloat64Number d) +{ + d += 0.5; + if (d <= 0) return 0; + if (d >= 255.0) return 255; + + return (cmsUInt8Number) _cmsQuickFloorWord(d); +} + + +// Return the size in bytes of a given formatter +static +cmsUInt32Number trueBytesSize(cmsUInt32Number Format) +{ + cmsUInt32Number fmt_bytes = T_BYTES(Format); + + // For double, the T_BYTES field returns zero + if (fmt_bytes == 0) + return sizeof(double); + + // Otherwise, it is already correct for all formats + return fmt_bytes; +} + + +// Several format converters + +typedef void(*cmsFormatterAlphaFn)(void* dst, const void* src); + + +// From 8 + +static +void copy8(void* dst, const void* src) +{ + memmove(dst, src, 1); +} + +static +void from8to16(void* dst, const void* src) +{ + cmsUInt8Number n = *(cmsUInt8Number*)src; + *(cmsUInt16Number*) dst = (cmsUInt16Number) FROM_8_TO_16(n); +} + +static +void from8to16SE(void* dst, const void* src) +{ + cmsUInt8Number n = *(cmsUInt8Number*)src; + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(FROM_8_TO_16(n)); +} + +static +void from8toFLT(void* dst, const void* src) +{ + *(cmsFloat32Number*)dst = (cmsFloat32Number) (*(cmsUInt8Number*)src) / 255.0f; +} + +static +void from8toDBL(void* dst, const void* src) +{ + *(cmsFloat64Number*)dst = (cmsFloat64Number) (*(cmsUInt8Number*)src) / 255.0; +} + +static +void from8toHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (*(cmsUInt8Number*)src) / 255.0f; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +// From 16 + +static +void from16to8(void* dst, const void* src) +{ + cmsUInt16Number n = *(cmsUInt16Number*)src; + *(cmsUInt8Number*) dst = FROM_16_TO_8(n); +} + +static +void from16SEto8(void* dst, const void* src) +{ + cmsUInt16Number n = *(cmsUInt16Number*)src; + *(cmsUInt8Number*)dst = FROM_16_TO_8(CHANGE_ENDIAN(n)); +} + +static +void copy16(void* dst, const void* src) +{ + memmove(dst, src, 2); +} + +static +void from16to16(void* dst, const void* src) +{ + cmsUInt16Number n = *(cmsUInt16Number*)src; + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(n); +} + +static +void from16toFLT(void* dst, const void* src) +{ + *(cmsFloat32Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f; +} + +static +void from16SEtoFLT(void* dst, const void* src) +{ + *(cmsFloat32Number*)dst = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f; +} + +static +void from16toDBL(void* dst, const void* src) +{ + *(cmsFloat64Number*)dst = (cmsFloat64Number) (*(cmsUInt16Number*)src) / 65535.0; +} + +static +void from16SEtoDBL(void* dst, const void* src) +{ + *(cmsFloat64Number*)dst = (cmsFloat64Number) (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0; +} + +static +void from16toHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (*(cmsUInt16Number*)src) / 65535.0f; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void from16SEtoHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} +// From Float + +static +void fromFLTto8(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); +} + +static +void fromFLTto16(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); +} + +static +void fromFLTto16SE(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f); + + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i); +} + +static +void copy32(void* dst, const void* src) +{ + memmove(dst, src, sizeof(cmsFloat32Number)); +} + +static +void fromFLTtoDBL(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsFloat64Number*)dst = (cmsFloat64Number)n; +} + +static +void fromFLTtoHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + + +// From HALF + +static +void fromHLFto8(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif + +} + +static +void fromHLFto16(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void fromHLFto16SE(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); + cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f); + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void fromHLFtoFLT(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + *(cmsFloat32Number*)dst = _cmsHalf2Float(*(cmsUInt16Number*)src); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void fromHLFtoDBL(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + *(cmsFloat64Number*)dst = (cmsFloat64Number)_cmsHalf2Float(*(cmsUInt16Number*)src); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +// From double +static +void fromDBLto8(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0); +} + +static +void fromDBLto16(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); +} + +static +void fromDBLto16SE(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f); + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i); +} + +static +void fromDBLtoFLT(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + *(cmsFloat32Number*)dst = (cmsFloat32Number) n; +} + +static +void fromDBLtoHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (cmsFloat32Number) *(cmsFloat64Number*)src; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void copy64(void* dst, const void* src) +{ + memmove(dst, src, sizeof(cmsFloat64Number)); +} + + +// Returns the position (x or y) of the formatter in the table of functions +static +int FormatterPos(cmsUInt32Number frm) +{ + cmsUInt32Number b = T_BYTES(frm); + + if (b == 0 && T_FLOAT(frm)) + return 5; // DBL +#ifndef CMS_NO_HALF_SUPPORT + if (b == 2 && T_FLOAT(frm)) + return 3; // HLF +#endif + if (b == 4 && T_FLOAT(frm)) + return 4; // FLT + if (b == 2 && !T_FLOAT(frm)) + { + if (T_ENDIAN16(frm)) + return 2; // 16SE + else + return 1; // 16 + } + if (b == 1 && !T_FLOAT(frm)) + return 0; // 8 + return -1; // not recognized +} + +// Obtains an alpha-to-alpha function formatter +static +cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out) +{ +static cmsFormatterAlphaFn FormattersAlpha[6][6] = { + + /* from 8 */ { copy8, from8to16, from8to16SE, from8toHLF, from8toFLT, from8toDBL }, + /* from 16*/ { from16to8, copy16, from16to16, from16toHLF, from16toFLT, from16toDBL }, + /* from 16SE*/{ from16SEto8, from16to16, copy16, from16SEtoHLF,from16SEtoFLT, from16SEtoDBL }, + /* from HLF*/ { fromHLFto8, fromHLFto16, fromHLFto16SE, copy16, fromHLFtoFLT, fromHLFtoDBL }, + /* from FLT*/ { fromFLTto8, fromFLTto16, fromFLTto16SE, fromFLTtoHLF, copy32, fromFLTtoDBL }, + /* from DBL*/ { fromDBLto8, fromDBLto16, fromDBLto16SE, fromDBLtoHLF, fromDBLtoFLT, copy64 }}; + + int in_n = FormatterPos(in); + int out_n = FormatterPos(out); + + if (in_n < 0 || out_n < 0 || in_n > 5 || out_n > 5) { + + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized alpha channel width"); + return NULL; + } + + return FormattersAlpha[in_n][out_n]; +} + + + +// This function computes the distance from each component to the next one in bytes. +static +void ComputeIncrementsForChunky(cmsUInt32Number Format, + cmsUInt32Number ComponentStartingOrder[], + cmsUInt32Number ComponentPointerIncrements[]) +{ + cmsUInt32Number channels[cmsMAXCHANNELS]; + cmsUInt32Number extra = T_EXTRA(Format); + cmsUInt32Number nchannels = T_CHANNELS(Format); + cmsUInt32Number total_chans = nchannels + extra; + cmsUInt32Number i; + cmsUInt32Number channelSize = trueBytesSize(Format); + cmsUInt32Number pixelSize = channelSize * total_chans; + + // Sanity check + if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) + return; + + memset(channels, 0, sizeof(channels)); + + // Separation is independent of starting point and only depends on channel size + for (i = 0; i < extra; i++) + ComponentPointerIncrements[i] = pixelSize; + + // Handle do swap + for (i = 0; i < total_chans; i++) + { + if (T_DOSWAP(Format)) { + channels[i] = total_chans - i - 1; + } + else { + channels[i] = i; + } + } + + // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012 + if (T_SWAPFIRST(Format) && total_chans > 1) { + + cmsUInt32Number tmp = channels[0]; + for (i = 0; i < total_chans-1; i++) + channels[i] = channels[i + 1]; + + channels[total_chans - 1] = tmp; + } + + // Handle size + if (channelSize > 1) + for (i = 0; i < total_chans; i++) { + channels[i] *= channelSize; + } + + for (i = 0; i < extra; i++) + ComponentStartingOrder[i] = channels[i + nchannels]; +} + + + +// On planar configurations, the distance is the stride added to any non-negative +static +void ComputeIncrementsForPlanar(cmsUInt32Number Format, + cmsUInt32Number BytesPerPlane, + cmsUInt32Number ComponentStartingOrder[], + cmsUInt32Number ComponentPointerIncrements[]) +{ + cmsUInt32Number channels[cmsMAXCHANNELS]; + cmsUInt32Number extra = T_EXTRA(Format); + cmsUInt32Number nchannels = T_CHANNELS(Format); + cmsUInt32Number total_chans = nchannels + extra; + cmsUInt32Number i; + cmsUInt32Number channelSize = trueBytesSize(Format); + + // Sanity check + if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) + return; + + memset(channels, 0, sizeof(channels)); + + // Separation is independent of starting point and only depends on channel size + for (i = 0; i < extra; i++) + ComponentPointerIncrements[i] = channelSize; + + // Handle do swap + for (i = 0; i < total_chans; i++) + { + if (T_DOSWAP(Format)) { + channels[i] = total_chans - i - 1; + } + else { + channels[i] = i; + } + } + + // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012 + if (T_SWAPFIRST(Format) && total_chans > 0) { + + cmsUInt32Number tmp = channels[0]; + for (i = 0; i < total_chans - 1; i++) + channels[i] = channels[i + 1]; + + channels[total_chans - 1] = tmp; + } + + // Handle size + for (i = 0; i < total_chans; i++) { + channels[i] *= BytesPerPlane; + } + + for (i = 0; i < extra; i++) + ComponentStartingOrder[i] = channels[i + nchannels]; +} + + + +// Dispatcher por chunky and planar RGB +static +void ComputeComponentIncrements(cmsUInt32Number Format, + cmsUInt32Number BytesPerPlane, + cmsUInt32Number ComponentStartingOrder[], + cmsUInt32Number ComponentPointerIncrements[]) +{ + if (T_PLANAR(Format)) { + + ComputeIncrementsForPlanar(Format, BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements); + } + else { + ComputeIncrementsForChunky(Format, ComponentStartingOrder, ComponentPointerIncrements); + } + +} + + + +// Handles extra channels copying alpha if requested by the flags +void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt32Number i, j, k; + cmsUInt32Number nExtra; + cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS]; + cmsUInt32Number SourceIncrements[cmsMAXCHANNELS]; + cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS]; + cmsUInt32Number DestIncrements[cmsMAXCHANNELS]; + + cmsFormatterAlphaFn copyValueFn; + + // Make sure we need some copy + if (!(p->dwOriginalFlags & cmsFLAGS_COPY_ALPHA)) + return; + + // Exit early if in-place color-management is occurring - no need to copy extra channels to themselves. + if (p->InputFormat == p->OutputFormat && in == out) + return; + + // Make sure we have same number of alpha channels. If not, just return as this should be checked at transform creation time. + nExtra = T_EXTRA(p->InputFormat); + if (nExtra != T_EXTRA(p->OutputFormat)) + return; + + // Anything to do? + if (nExtra == 0) + return; + + // Compute the increments + ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements); + ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements); + + // Check for conversions 8, 16, half, float, dbl + copyValueFn = _cmsGetFormatterAlpha(p->ContextID, p->InputFormat, p->OutputFormat); + if (copyValueFn == NULL) + return; + + if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly + + cmsUInt8Number* SourcePtr; + cmsUInt8Number* DestPtr; + + cmsUInt32Number SourceStrideIncrement = 0; + cmsUInt32Number DestStrideIncrement = 0; + + // The loop itself + for (i = 0; i < LineCount; i++) { + + // Prepare pointers for the loop + SourcePtr = (cmsUInt8Number*)in + SourceStartingOrder[0] + SourceStrideIncrement; + DestPtr = (cmsUInt8Number*)out + DestStartingOrder[0] + DestStrideIncrement; + + for (j = 0; j < PixelsPerLine; j++) { + + copyValueFn(DestPtr, SourcePtr); + + SourcePtr += SourceIncrements[0]; + DestPtr += DestIncrements[0]; + } + + SourceStrideIncrement += Stride->BytesPerLineIn; + DestStrideIncrement += Stride->BytesPerLineOut; + } + + } + else { // General case with more than one extra channel + + cmsUInt8Number* SourcePtr[cmsMAXCHANNELS]; + cmsUInt8Number* DestPtr[cmsMAXCHANNELS]; + + cmsUInt32Number SourceStrideIncrements[cmsMAXCHANNELS]; + cmsUInt32Number DestStrideIncrements[cmsMAXCHANNELS]; + + memset(SourceStrideIncrements, 0, sizeof(SourceStrideIncrements)); + memset(DestStrideIncrements, 0, sizeof(DestStrideIncrements)); + + // The loop itself + for (i = 0; i < LineCount; i++) { + + // Prepare pointers for the loop + for (j = 0; j < nExtra; j++) { + + SourcePtr[j] = (cmsUInt8Number*)in + SourceStartingOrder[j] + SourceStrideIncrements[j]; + DestPtr[j] = (cmsUInt8Number*)out + DestStartingOrder[j] + DestStrideIncrements[j]; + } + + for (j = 0; j < PixelsPerLine; j++) { + + for (k = 0; k < nExtra; k++) { + + copyValueFn(DestPtr[k], SourcePtr[k]); + + SourcePtr[k] += SourceIncrements[k]; + DestPtr[k] += DestIncrements[k]; + } + } + + for (j = 0; j < nExtra; j++) { + + SourceStrideIncrements[j] += Stride->BytesPerLineIn; + DestStrideIncrements[j] += Stride->BytesPerLineOut; + } + } + } +} + + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscam02.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscam02.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscam02.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscam02.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,515 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging. + +// ---------- Implementation -------------------------------------------- + +typedef struct { + + cmsFloat64Number XYZ[3]; + cmsFloat64Number RGB[3]; + cmsFloat64Number RGBc[3]; + cmsFloat64Number RGBp[3]; + cmsFloat64Number RGBpa[3]; + cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M; + cmsFloat64Number abC[2]; + cmsFloat64Number abs[2]; + cmsFloat64Number abM[2]; + +} CAM02COLOR; + +typedef struct { + + CAM02COLOR adoptedWhite; + cmsFloat64Number LA, Yb; + cmsFloat64Number F, c, Nc; + cmsUInt32Number surround; + cmsFloat64Number n, Nbb, Ncb, z, FL, D; + + cmsContext ContextID; + +} cmsCIECAM02; + + +static +cmsFloat64Number compute_n(cmsCIECAM02* pMod) +{ + return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]); +} + +static +cmsFloat64Number compute_z(cmsCIECAM02* pMod) +{ + return (1.48 + pow(pMod -> n, 0.5)); +} + +static +cmsFloat64Number computeNbb(cmsCIECAM02* pMod) +{ + return (0.725 * pow((1.0 / pMod -> n), 0.2)); +} + +static +cmsFloat64Number computeFL(cmsCIECAM02* pMod) +{ + cmsFloat64Number k, FL; + + k = 1.0 / ((5.0 * pMod->LA) + 1.0); + FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 * + (pow((1.0 - pow(k, 4.0)), 2.0)) * + (pow((5.0 * pMod->LA), (1.0 / 3.0))); + + return FL; +} + +static +cmsFloat64Number computeD(cmsCIECAM02* pMod) +{ + cmsFloat64Number D; + + D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0))); + + return D; +} + + +static +CAM02COLOR XYZtoCAT02(CAM02COLOR clr) +{ + clr.RGB[0] = (clr.XYZ[0] * 0.7328) + (clr.XYZ[1] * 0.4296) + (clr.XYZ[2] * -0.1624); + clr.RGB[1] = (clr.XYZ[0] * -0.7036) + (clr.XYZ[1] * 1.6975) + (clr.XYZ[2] * 0.0061); + clr.RGB[2] = (clr.XYZ[0] * 0.0030) + (clr.XYZ[1] * 0.0136) + (clr.XYZ[2] * 0.9834); + + return clr; +} + +static +CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + + for (i = 0; i < 3; i++) { + clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] * + (pMod->D / pMod -> adoptedWhite.RGB[i])) + + (1.0 - pMod->D)) * clr.RGB[i]; + } + + return clr; +} + + +static +CAM02COLOR CAT02toHPE(CAM02COLOR clr) +{ + cmsFloat64Number M[9]; + + M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628)); + M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698)); + M[2] =(( 0.38971 * 0.182745) + (0.68898 * 0.072098) + (-0.07868 * 1.015326)); + M[3] =((-0.22981 * 1.096124) + (1.18340 * 0.454369) + ( 0.04641 * -0.009628)); + M[4] =((-0.22981 * -0.278869) + (1.18340 * 0.473533) + ( 0.04641 * -0.005698)); + M[5] =((-0.22981 * 0.182745) + (1.18340 * 0.072098) + ( 0.04641 * 1.015326)); + M[6] =(-0.009628); + M[7] =(-0.005698); + M[8] =( 1.015326); + + clr.RGBp[0] = (clr.RGBc[0] * M[0]) + (clr.RGBc[1] * M[1]) + (clr.RGBc[2] * M[2]); + clr.RGBp[1] = (clr.RGBc[0] * M[3]) + (clr.RGBc[1] * M[4]) + (clr.RGBc[2] * M[5]); + clr.RGBp[2] = (clr.RGBc[0] * M[6]) + (clr.RGBc[1] * M[7]) + (clr.RGBc[2] * M[8]); + + return clr; +} + +static +CAM02COLOR NonlinearCompression(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + cmsFloat64Number temp; + + for (i = 0; i < 3; i++) { + if (clr.RGBp[i] < 0) { + + temp = pow((-1.0 * pMod->FL * clr.RGBp[i] / 100.0), 0.42); + clr.RGBpa[i] = (-1.0 * 400.0 * temp) / (temp + 27.13) + 0.1; + } + else { + temp = pow((pMod->FL * clr.RGBp[i] / 100.0), 0.42); + clr.RGBpa[i] = (400.0 * temp) / (temp + 27.13) + 0.1; + } + } + + clr.A = (((2.0 * clr.RGBpa[0]) + clr.RGBpa[1] + + (clr.RGBpa[2] / 20.0)) - 0.305) * pMod->Nbb; + + return clr; +} + +static +CAM02COLOR ComputeCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsFloat64Number a, b, temp, e, t, r2d, d2r; + + a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0); + b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0; + + r2d = (180.0 / 3.141592654); + if (a == 0) { + if (b == 0) clr.h = 0; + else if (b > 0) clr.h = 90; + else clr.h = 270; + } + else if (a > 0) { + temp = b / a; + if (b > 0) clr.h = (r2d * atan(temp)); + else if (b == 0) clr.h = 0; + else clr.h = (r2d * atan(temp)) + 360; + } + else { + temp = b / a; + clr.h = (r2d * atan(temp)) + 180; + } + + d2r = (3.141592654 / 180.0); + e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) * + (cos((clr.h * d2r + 2.0)) + 3.8); + + if (clr.h < 20.14) { + temp = ((clr.h + 122.47)/1.2) + ((20.14 - clr.h)/0.8); + clr.H = 300 + (100*((clr.h + 122.47)/1.2)) / temp; + } + else if (clr.h < 90.0) { + temp = ((clr.h - 20.14)/0.8) + ((90.00 - clr.h)/0.7); + clr.H = (100*((clr.h - 20.14)/0.8)) / temp; + } + else if (clr.h < 164.25) { + temp = ((clr.h - 90.00)/0.7) + ((164.25 - clr.h)/1.0); + clr.H = 100 + ((100*((clr.h - 90.00)/0.7)) / temp); + } + else if (clr.h < 237.53) { + temp = ((clr.h - 164.25)/1.0) + ((237.53 - clr.h)/1.2); + clr.H = 200 + ((100*((clr.h - 164.25)/1.0)) / temp); + } + else { + temp = ((clr.h - 237.53)/1.2) + ((360 - clr.h + 20.14)/0.8); + clr.H = 300 + ((100*((clr.h - 237.53)/1.2)) / temp); + } + + clr.J = 100.0 * pow((clr.A / pMod->adoptedWhite.A), + (pMod->c * pMod->z)); + + clr.Q = (4.0 / pMod->c) * pow((clr.J / 100.0), 0.5) * + (pMod->adoptedWhite.A + 4.0) * pow(pMod->FL, 0.25); + + t = (e * pow(((a * a) + (b * b)), 0.5)) / + (clr.RGBpa[0] + clr.RGBpa[1] + + ((21.0 / 20.0) * clr.RGBpa[2])); + + clr.C = pow(t, 0.9) * pow((clr.J / 100.0), 0.5) * + pow((1.64 - pow(0.29, pMod->n)), 0.73); + + clr.M = clr.C * pow(pMod->FL, 0.25); + clr.s = 100.0 * pow((clr.M / clr.Q), 0.5); + + return clr; +} + + +static +CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + + cmsFloat64Number t, e, p1, p2, p3, p4, p5, hr, d2r; + d2r = 3.141592654 / 180.0; + + t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) * + (pow((1.64 - pow(0.29, pMod->n)), 0.73)))), + (1.0 / 0.9) ); + e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) * + (cos((clr.h * d2r + 2.0)) + 3.8); + + clr.A = pMod->adoptedWhite.A * pow( + (clr.J / 100.0), + (1.0 / (pMod->c * pMod->z))); + + p1 = e / t; + p2 = (clr.A / pMod->Nbb) + 0.305; + p3 = 21.0 / 20.0; + + hr = clr.h * d2r; + + if (fabs(sin(hr)) >= fabs(cos(hr))) { + p4 = p1 / sin(hr); + clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) / + (p4 + (2.0 + p3) * (220.0 / 1403.0) * + (cos(hr) / sin(hr)) - (27.0 / 1403.0) + + p3 * (6300.0 / 1403.0)); + clr.a = clr.b * (cos(hr) / sin(hr)); + } + else { + p5 = p1 / cos(hr); + clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) / + (p5 + (2.0 + p3) * (220.0 / 1403.0) - + ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) * + (sin(hr) / cos(hr))); + clr.b = clr.a * (sin(hr) / cos(hr)); + } + + clr.RGBpa[0] = ((460.0 / 1403.0) * p2) + + ((451.0 / 1403.0) * clr.a) + + ((288.0 / 1403.0) * clr.b); + clr.RGBpa[1] = ((460.0 / 1403.0) * p2) - + ((891.0 / 1403.0) * clr.a) - + ((261.0 / 1403.0) * clr.b); + clr.RGBpa[2] = ((460.0 / 1403.0) * p2) - + ((220.0 / 1403.0) * clr.a) - + ((6300.0 / 1403.0) * clr.b); + + return clr; +} + +static +CAM02COLOR InverseNonlinearity(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + cmsFloat64Number c1; + + for (i = 0; i < 3; i++) { + if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1; + else c1 = 1; + clr.RGBp[i] = c1 * (100.0 / pMod->FL) * + pow(((27.13 * fabs(clr.RGBpa[i] - 0.1)) / + (400.0 - fabs(clr.RGBpa[i] - 0.1))), + (1.0 / 0.42)); + } + + return clr; +} + +static +CAM02COLOR HPEtoCAT02(CAM02COLOR clr) +{ + cmsFloat64Number M[9]; + + M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950)); + M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054)); + M[2] = (( 0.7328 * 0.201908) + (0.4296 * 0.000008) - 0.1624); + M[3] = ((-0.7036 * 1.910197) + (1.6975 * 0.370950)); + M[4] = ((-0.7036 * -1.112124) + (1.6975 * 0.629054)); + M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061); + M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950)); + M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054)); + M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);; + + clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]); + clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]); + clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]); + return clr; +} + + +static +CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + for (i = 0; i < 3; i++) { + clr.RGB[i] = clr.RGBc[i] / + ((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D); + } + return clr; +} + + +static +CAM02COLOR CAT02toXYZ(CAM02COLOR clr) +{ + clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745); + clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098); + clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326); + + return clr; +} + + +cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC) +{ + cmsCIECAM02* lpMod; + + _cmsAssert(pVC != NULL); + + if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) { + return NULL; + } + + lpMod ->ContextID = ContextID; + + lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X; + lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y; + lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z; + + lpMod -> LA = pVC ->La; + lpMod -> Yb = pVC ->Yb; + lpMod -> D = pVC ->D_value; + lpMod -> surround = pVC ->surround; + + switch (lpMod -> surround) { + + + case CUTSHEET_SURROUND: + lpMod->F = 0.8; + lpMod->c = 0.41; + lpMod->Nc = 0.8; + break; + + case DARK_SURROUND: + lpMod -> F = 0.8; + lpMod -> c = 0.525; + lpMod -> Nc = 0.8; + break; + + case DIM_SURROUND: + lpMod -> F = 0.9; + lpMod -> c = 0.59; + lpMod -> Nc = 0.95; + break; + + default: + // Average surround + lpMod -> F = 1.0; + lpMod -> c = 0.69; + lpMod -> Nc = 1.0; + } + + lpMod -> n = compute_n(lpMod); + lpMod -> z = compute_z(lpMod); + lpMod -> Nbb = computeNbb(lpMod); + lpMod -> FL = computeFL(lpMod); + + if (lpMod -> D == D_CALCULATE) { + lpMod -> D = computeD(lpMod); + } + + lpMod -> Ncb = lpMod -> Nbb; + + lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite); + lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod); + lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite); + lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod); + + return (cmsHANDLE) lpMod; + +} + +void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel) +{ + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + if (lpMod) _cmsFree(lpMod ->ContextID, lpMod); +} + + +void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut) +{ + CAM02COLOR clr; + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + _cmsAssert(lpMod != NULL); + _cmsAssert(pIn != NULL); + _cmsAssert(pOut != NULL); + + memset(&clr, 0, sizeof(clr)); + + clr.XYZ[0] = pIn ->X; + clr.XYZ[1] = pIn ->Y; + clr.XYZ[2] = pIn ->Z; + + clr = XYZtoCAT02(clr); + clr = ChromaticAdaptation(clr, lpMod); + clr = CAT02toHPE(clr); + clr = NonlinearCompression(clr, lpMod); + clr = ComputeCorrelates(clr, lpMod); + + pOut ->J = clr.J; + pOut ->C = clr.C; + pOut ->h = clr.h; +} + +void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut) +{ + CAM02COLOR clr; + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + _cmsAssert(lpMod != NULL); + _cmsAssert(pIn != NULL); + _cmsAssert(pOut != NULL); + + memset(&clr, 0, sizeof(clr)); + + clr.J = pIn -> J; + clr.C = pIn -> C; + clr.h = pIn -> h; + + clr = InverseCorrelates(clr, lpMod); + clr = InverseNonlinearity(clr, lpMod); + clr = HPEtoCAT02(clr); + clr = InverseChromaticAdaptation(clr, lpMod); + clr = CAT02toXYZ(clr); + + pOut ->X = clr.XYZ[0]; + pOut ->Y = clr.XYZ[1]; + pOut ->Z = clr.XYZ[2]; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscgats.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscgats.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscgats.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscgats.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,2815 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- + + +#define MAXID 128 // Max length of identifier +#define MAXSTR 1024 // Max length of string +#define MAXTABLES 255 // Max Number of tables in a single stream +#define MAXINCLUDE 20 // Max number of nested includes + +#define DEFAULT_DBL_FORMAT "%.10g" // Double formatting + +#ifdef CMS_IS_WINDOWS_ +# include +# define DIR_CHAR '\\' +#else +# define DIR_CHAR '/' +#endif + + +// Symbols +typedef enum { + + SUNDEFINED, + SINUM, // Integer + SDNUM, // Real + SIDENT, // Identifier + SSTRING, // string + SCOMMENT, // comment + SEOLN, // End of line + SEOF, // End of stream + SSYNERROR, // Syntax error found on stream + + // Keywords + + SBEGIN_DATA, + SBEGIN_DATA_FORMAT, + SEND_DATA, + SEND_DATA_FORMAT, + SKEYWORD, + SDATA_FORMAT_ID, + SINCLUDE + + } SYMBOL; + + +// How to write the value +typedef enum { + + WRITE_UNCOOKED, + WRITE_STRINGIFY, + WRITE_HEXADECIMAL, + WRITE_BINARY, + WRITE_PAIR + + } WRITEMODE; + +// Linked list of variable names +typedef struct _KeyVal { + + struct _KeyVal* Next; + char* Keyword; // Name of variable + struct _KeyVal* NextSubkey; // If key is a dictionary, points to the next item + char* Subkey; // If key is a dictionary, points to the subkey name + char* Value; // Points to value + WRITEMODE WriteAs; // How to write the value + + } KEYVALUE; + + +// Linked list of memory chunks (Memory sink) +typedef struct _OwnedMem { + + struct _OwnedMem* Next; + void * Ptr; // Point to value + + } OWNEDMEM; + +// Suballocator +typedef struct _SubAllocator { + + cmsUInt8Number* Block; + cmsUInt32Number BlockSize; + cmsUInt32Number Used; + + } SUBALLOCATOR; + +// Table. Each individual table can hold properties and rows & cols +typedef struct _Table { + + char SheetType[MAXSTR]; // The first row of the IT8 (the type) + + int nSamples, nPatches; // Cols, Rows + int SampleID; // Pos of ID + + KEYVALUE* HeaderList; // The properties + + char** DataFormat; // The binary stream descriptor + char** Data; // The binary stream + + } TABLE; + +// File stream being parsed +typedef struct _FileContext { + char FileName[cmsMAX_PATH]; // File name if being read from file + FILE* Stream; // File stream or NULL if holded in memory + } FILECTX; + +// This struct hold all information about an open IT8 handler. +typedef struct { + + + cmsUInt32Number TablesCount; // How many tables in this stream + cmsUInt32Number nTable; // The actual table + + TABLE Tab[MAXTABLES]; + + // Memory management + OWNEDMEM* MemorySink; // The storage backend + SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast + + // Parser state machine + SYMBOL sy; // Current symbol + int ch; // Current character + + cmsInt32Number inum; // integer value + cmsFloat64Number dnum; // real value + + char id[MAXID]; // identifier + char str[MAXSTR]; // string + + // Allowed keywords & datasets. They have visibility on whole stream + KEYVALUE* ValidKeywords; + KEYVALUE* ValidSampleID; + + char* Source; // Points to loc. being parsed + cmsInt32Number lineno; // line counter for error reporting + + FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed + cmsInt32Number IncludeSP; // Include Stack Pointer + + char* MemoryBlock; // The stream if holded in memory + + char DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter + + cmsContext ContextID; // The threading context + + } cmsIT8; + + +// The stream for save operations +typedef struct { + + FILE* stream; // For save-to-file behaviour + + cmsUInt8Number* Base; + cmsUInt8Number* Ptr; // For save-to-mem behaviour + cmsUInt32Number Used; + cmsUInt32Number Max; + + } SAVESTREAM; + + +// ------------------------------------------------------ cmsIT8 parsing routines + + +// A keyword +typedef struct { + + const char *id; + SYMBOL sy; + + } KEYWORD; + +// The keyword->symbol translation table. Sorting is required. +static const KEYWORD TabKeys[] = { + + {"$INCLUDE", SINCLUDE}, // This is an extension! + {".INCLUDE", SINCLUDE}, // This is an extension! + + {"BEGIN_DATA", SBEGIN_DATA }, + {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT }, + {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID}, + {"END_DATA", SEND_DATA}, + {"END_DATA_FORMAT", SEND_DATA_FORMAT}, + {"KEYWORD", SKEYWORD} + }; + +#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD)) + +// Predefined properties + +// A property +typedef struct { + const char *id; // The identifier + WRITEMODE as; // How is supposed to be written + } PROPERTY; + +static PROPERTY PredefinedProperties[] = { + + {"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS + {"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS + {"ORIGINATOR", WRITE_STRINGIFY}, // Required - Identifies the specific system, organization or individual that created the data file. + {"FILE_DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file. + {"CREATED", WRITE_STRINGIFY}, // Required - Indicates date of creation of the data file. + {"DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file. + {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, // The diffuse geometry used. Allowed values are "sphere" or "opal". + {"MANUFACTURER", WRITE_STRINGIFY}, + {"MANUFACTURE", WRITE_STRINGIFY}, // Some broken Fuji targets does store this value + {"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm. + {"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target. + + {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code + // uniquely identifying th e material. This is intend ed to be used for IT8.7 + // physical targets only (i.e . IT8.7/1 a nd IT8.7/2). + + {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and + // model number) to generate the data reported. This data will often + // provide more information about the particular data collected than an + // extensive list of specific details. This is particularly important for + // spectral data or data derived from spectrophotometry. + + {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide + // a guide to the potential for issues of paper fluorescence, etc. + + {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported. + // Where standard conditions have been defined (e.g., SWOP at nominal) + // named conditions may suffice. Otherwise, detailed information is + // needed. + + {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during + // measurement. Allowed values are "black", "white", or {"na". + + {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic + // below properties are new in recent specs: + + {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated + // along with details of the geometry and the aperture size and shape. For example, + // for transmission measurements it is important to identify 0/diffuse, diffuse/0, + // opal or integrating sphere, etc. For reflection it is important to identify 0/45, + // 45/0, sphere (specular included or excluded), etc. + + {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to + // denote the use of filters such as none, D65, Red, Green or Blue. + + {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed + // values are {"yes", "white", "none" or "na". + + {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the + // calculation of various data parameters (2 degree and 10 degree), CIE standard + // illuminant functions used in the calculation of various data parameters (e.g., D50, + // D65, etc.), density status response, etc. If used there shall be at least one + // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute + // in the set shall be {"name" and shall identify the particular parameter used. + // The second shall be {"value" and shall provide the value associated with that name. + // For ASCII data, a string containing the Name and Value attribute pairs shall follow + // the weighting function keyword. A semi-colon separates attribute pairs from each + // other and within the attribute the name and value are separated by a comma. + + {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name + // of the calculation, parameter is the name of the parameter used in the calculation + // and value is the value of the parameter. + + {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc. + + {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target. + + {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table. + + {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table. +}; + +#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY)) + + +// Predefined sample types on dataset +static const char* PredefinedSampleID[] = { + "SAMPLE_ID", // Identifies sample that data represents + "STRING", // Identifies label, or other non-machine readable value. + // Value must begin and end with a " symbol + + "CMYK_C", // Cyan component of CMYK data expressed as a percentage + "CMYK_M", // Magenta component of CMYK data expressed as a percentage + "CMYK_Y", // Yellow component of CMYK data expressed as a percentage + "CMYK_K", // Black component of CMYK data expressed as a percentage + "D_RED", // Red filter density + "D_GREEN", // Green filter density + "D_BLUE", // Blue filter density + "D_VIS", // Visual filter density + "D_MAJOR_FILTER", // Major filter d ensity + "RGB_R", // Red component of RGB data + "RGB_G", // Green component of RGB data + "RGB_B", // Blue com ponent of RGB data + "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers + "SPECTRAL_PCT", // Percentage reflectance/transmittance + "SPECTRAL_DEC", // Reflectance/transmittance + "XYZ_X", // X component of tristimulus data + "XYZ_Y", // Y component of tristimulus data + "XYZ_Z", // Z component of tristimulus data + "XYY_X", // x component of chromaticity data + "XYY_Y", // y component of chromaticity data + "XYY_CAPY", // Y component of tristimulus data + "LAB_L", // L* component of Lab data + "LAB_A", // a* component of Lab data + "LAB_B", // b* component of Lab data + "LAB_C", // C*ab component of Lab data + "LAB_H", // hab component of Lab data + "LAB_DE", // CIE dE + "LAB_DE_94", // CIE dE using CIE 94 + "LAB_DE_CMC", // dE using CMC + "LAB_DE_2000", // CIE dE using CIE DE 2000 + "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average + // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets) + "STDEV_X", // Standard deviation of X (tristimulus data) + "STDEV_Y", // Standard deviation of Y (tristimulus data) + "STDEV_Z", // Standard deviation of Z (tristimulus data) + "STDEV_L", // Standard deviation of L* + "STDEV_A", // Standard deviation of a* + "STDEV_B", // Standard deviation of b* + "STDEV_DE", // Standard deviation of CIE dE + "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is + // used to derive an estimate of the chi-squared parameter which is + // recommended as the predictor of the variability of dE + +#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *)) + +//Forward declaration of some internal functions +static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size); + +// Checks whatever c is a separator +static +cmsBool isseparator(int c) +{ + return (c == ' ') || (c == '\t') ; +} + +// Checks whatever c is a valid identifier char +static +cmsBool ismiddle(int c) +{ + return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127)); +} + +// Checks whatsever c is a valid identifier middle char. +static +cmsBool isidchar(int c) +{ + return isalnum(c) || ismiddle(c); +} + +// Checks whatsever c is a valid identifier first char. +static +cmsBool isfirstidchar(int c) +{ + return !isdigit(c) && ismiddle(c); +} + +// Guess whether the supplied path looks like an absolute path +static +cmsBool isabsolutepath(const char *path) +{ + char ThreeChars[4]; + + if(path == NULL) + return FALSE; + if (path[0] == 0) + return FALSE; + + strncpy(ThreeChars, path, 3); + ThreeChars[3] = 0; + + if(ThreeChars[0] == DIR_CHAR) + return TRUE; + +#ifdef CMS_IS_WINDOWS_ + if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':') + return TRUE; +#endif + return FALSE; +} + + +// Makes a file path based on a given reference path +// NOTE: this function doesn't check if the path exists or even if it's legal +static +cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen) +{ + char *tail; + cmsUInt32Number len; + + // Already absolute? + if (isabsolutepath(relPath)) { + + strncpy(buffer, relPath, MaxLen); + buffer[MaxLen-1] = 0; + return TRUE; + } + + // No, search for last + strncpy(buffer, basePath, MaxLen); + buffer[MaxLen-1] = 0; + + tail = strrchr(buffer, DIR_CHAR); + if (tail == NULL) return FALSE; // Is not absolute and has no separators?? + + len = (cmsUInt32Number) (tail - buffer); + if (len >= MaxLen) return FALSE; + + // No need to assure zero terminator over here + strncpy(tail + 1, relPath, MaxLen - len); + + return TRUE; +} + + +// Make sure no exploit is being even tried +static +const char* NoMeta(const char* str) +{ + if (strchr(str, '%') != NULL) + return "**** CORRUPTED FORMAT STRING ***"; + + return str; +} + +// Syntax error +static +cmsBool SynError(cmsIT8* it8, const char *Txt, ...) +{ + char Buffer[256], ErrMsg[1024]; + va_list args; + + va_start(args, Txt); + vsnprintf(Buffer, 255, Txt, args); + Buffer[255] = 0; + va_end(args); + + snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer); + ErrMsg[1023] = 0; + it8->sy = SSYNERROR; + cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg); + return FALSE; +} + +// Check if current symbol is same as specified. issue an error else. +static +cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err) +{ + if (it8 -> sy != sy) + return SynError(it8, NoMeta(Err)); + return TRUE; +} + +// Read Next character from stream +static +void NextCh(cmsIT8* it8) +{ + if (it8 -> FileStack[it8 ->IncludeSP]->Stream) { + + it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream); + + if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) { + + if (it8 ->IncludeSP > 0) { + + fclose(it8 ->FileStack[it8->IncludeSP--]->Stream); + it8 -> ch = ' '; // Whitespace to be ignored + + } else + it8 ->ch = 0; // EOF + } + } + else { + it8->ch = *it8->Source; + if (it8->ch) it8->Source++; + } +} + + +// Try to see if current identifier is a keyword, if so return the referred symbol +static +SYMBOL BinSrchKey(const char *id) +{ + int l = 1; + int r = NUMKEYS; + int x, res; + + while (r >= l) + { + x = (l+r)/2; + res = cmsstrcasecmp(id, TabKeys[x-1].id); + if (res == 0) return TabKeys[x-1].sy; + if (res < 0) r = x - 1; + else l = x + 1; + } + + return SUNDEFINED; +} + + +// 10 ^n +static +cmsFloat64Number xpow10(int n) +{ + return pow(10, (cmsFloat64Number) n); +} + + +// Reads a Real number, tries to follow from integer number +static +void ReadReal(cmsIT8* it8, cmsInt32Number inum) +{ + it8->dnum = (cmsFloat64Number)inum; + + while (isdigit(it8->ch)) { + + it8->dnum = (cmsFloat64Number)it8->dnum * 10.0 + (cmsFloat64Number)(it8->ch - '0'); + NextCh(it8); + } + + if (it8->ch == '.') { // Decimal point + + cmsFloat64Number frac = 0.0; // fraction + int prec = 0; // precision + + NextCh(it8); // Eats dec. point + + while (isdigit(it8->ch)) { + + frac = frac * 10.0 + (cmsFloat64Number)(it8->ch - '0'); + prec++; + NextCh(it8); + } + + it8->dnum = it8->dnum + (frac / xpow10(prec)); + } + + // Exponent, example 34.00E+20 + if (toupper(it8->ch) == 'E') { + + cmsInt32Number e; + cmsInt32Number sgn; + + NextCh(it8); sgn = 1; + + if (it8->ch == '-') { + + sgn = -1; NextCh(it8); + } + else + if (it8->ch == '+') { + + sgn = +1; + NextCh(it8); + } + + e = 0; + while (isdigit(it8->ch)) { + + cmsInt32Number digit = (it8->ch - '0'); + + if ((cmsFloat64Number)e * 10.0 + (cmsFloat64Number)digit < (cmsFloat64Number)+2147483647.0) + e = e * 10 + digit; + + NextCh(it8); + } + + e = sgn*e; + it8->dnum = it8->dnum * xpow10(e); + } +} + +// Parses a float number +// This can not call directly atof because it uses locale dependent +// parsing, while CCMX files always use . as decimal separator +static +cmsFloat64Number ParseFloatNumber(const char *Buffer) +{ + cmsFloat64Number dnum = 0.0; + int sign = 1; + + // keep safe + if (Buffer == NULL) return 0.0; + + if (*Buffer == '-' || *Buffer == '+') { + + sign = (*Buffer == '-') ? -1 : 1; + Buffer++; + } + + + while (*Buffer && isdigit((int)*Buffer)) { + + dnum = dnum * 10.0 + (*Buffer - '0'); + if (*Buffer) Buffer++; + } + + if (*Buffer == '.') { + + cmsFloat64Number frac = 0.0; // fraction + int prec = 0; // precision + + if (*Buffer) Buffer++; + + while (*Buffer && isdigit((int)*Buffer)) { + + frac = frac * 10.0 + (*Buffer - '0'); + prec++; + if (*Buffer) Buffer++; + } + + dnum = dnum + (frac / xpow10(prec)); + } + + // Exponent, example 34.00E+20 + if (*Buffer && toupper(*Buffer) == 'E') { + + int e; + int sgn; + + if (*Buffer) Buffer++; + sgn = 1; + + if (*Buffer == '-') { + + sgn = -1; + if (*Buffer) Buffer++; + } + else + if (*Buffer == '+') { + + sgn = +1; + if (*Buffer) Buffer++; + } + + e = 0; + while (*Buffer && isdigit((int)*Buffer)) { + + cmsInt32Number digit = (*Buffer - '0'); + + if ((cmsFloat64Number)e * 10.0 + digit < (cmsFloat64Number)+2147483647.0) + e = e * 10 + digit; + + if (*Buffer) Buffer++; + } + + e = sgn*e; + dnum = dnum * xpow10(e); + } + + return sign * dnum; +} + + +// Reads next symbol +static +void InSymbol(cmsIT8* it8) +{ + CMSREGISTER char *idptr; + CMSREGISTER int k; + SYMBOL key; + int sng; + + do { + + while (isseparator(it8->ch)) + NextCh(it8); + + if (isfirstidchar(it8->ch)) { // Identifier + + k = 0; + idptr = it8->id; + + do { + + if (++k < MAXID) *idptr++ = (char) it8->ch; + + NextCh(it8); + + } while (isidchar(it8->ch)); + + *idptr = '\0'; + + + key = BinSrchKey(it8->id); + if (key == SUNDEFINED) it8->sy = SIDENT; + else it8->sy = key; + + } + else // Is a number? + if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+') + { + int sign = 1; + + if (it8->ch == '-') { + sign = -1; + NextCh(it8); + } + + it8->inum = 0; + it8->sy = SINUM; + + if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary) + + NextCh(it8); + if (toupper(it8->ch) == 'X') { + + int j; + + NextCh(it8); + while (isxdigit(it8->ch)) + { + it8->ch = toupper(it8->ch); + if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10; + else j = it8->ch - '0'; + + if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0) + { + SynError(it8, "Invalid hexadecimal number"); + return; + } + + it8->inum = it8->inum * 16 + j; + NextCh(it8); + } + return; + } + + if (toupper(it8->ch) == 'B') { // Binary + + int j; + + NextCh(it8); + while (it8->ch == '0' || it8->ch == '1') + { + j = it8->ch - '0'; + + if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0) + { + SynError(it8, "Invalid binary number"); + return; + } + + it8->inum = it8->inum * 2 + j; + NextCh(it8); + } + return; + } + } + + + while (isdigit(it8->ch)) { + + cmsInt32Number digit = (it8->ch - '0'); + + if ((cmsFloat64Number) it8->inum * 10.0 + (cmsFloat64Number) digit > (cmsFloat64Number) +2147483647.0) { + ReadReal(it8, it8->inum); + it8->sy = SDNUM; + it8->dnum *= sign; + return; + } + + it8->inum = it8->inum * 10 + digit; + NextCh(it8); + } + + if (it8->ch == '.') { + + ReadReal(it8, it8->inum); + it8->sy = SDNUM; + it8->dnum *= sign; + return; + } + + it8 -> inum *= sign; + + // Special case. Numbers followed by letters are taken as identifiers + + if (isidchar(it8 ->ch)) { + + if (it8 ->sy == SINUM) { + + snprintf(it8->id, 127, "%d", it8->inum); + } + else { + + snprintf(it8->id, 127, it8 ->DoubleFormatter, it8->dnum); + } + + k = (int) strlen(it8 ->id); + idptr = it8 ->id + k; + do { + + if (++k < MAXID) *idptr++ = (char) it8->ch; + + NextCh(it8); + + } while (isidchar(it8->ch)); + + *idptr = '\0'; + it8->sy = SIDENT; + } + return; + + } + else + switch ((int) it8->ch) { + + // EOF marker -- ignore it + case '\x1a': + NextCh(it8); + break; + + // Eof stream markers + case 0: + case -1: + it8->sy = SEOF; + break; + + + // Next line + case '\r': + NextCh(it8); + if (it8 ->ch == '\n') + NextCh(it8); + it8->sy = SEOLN; + it8->lineno++; + break; + + case '\n': + NextCh(it8); + it8->sy = SEOLN; + it8->lineno++; + break; + + // Comment + case '#': + NextCh(it8); + while (it8->ch && it8->ch != '\n' && it8->ch != '\r') + NextCh(it8); + + it8->sy = SCOMMENT; + break; + + // String. + case '\'': + case '\"': + idptr = it8->str; + sng = it8->ch; + k = 0; + NextCh(it8); + + while (k < (MAXSTR-1) && it8->ch != sng) { + + if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1; + else { + *idptr++ = (char) it8->ch; + NextCh(it8); + k++; + } + } + + it8->sy = SSTRING; + *idptr = '\0'; + NextCh(it8); + break; + + + default: + SynError(it8, "Unrecognized character: 0x%x", it8 ->ch); + return; + } + + } while (it8->sy == SCOMMENT); + + // Handle the include special token + + if (it8 -> sy == SINCLUDE) { + + FILECTX* FileNest; + + if(it8 -> IncludeSP >= (MAXINCLUDE-1)) { + + SynError(it8, "Too many recursion levels"); + return; + } + + InSymbol(it8); + if (!Check(it8, SSTRING, "Filename expected")) return; + + FileNest = it8 -> FileStack[it8 -> IncludeSP + 1]; + if(FileNest == NULL) { + + FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); + //if(FileNest == NULL) + // TODO: how to manage out-of-memory conditions? + } + + if (BuildAbsolutePath(it8->str, + it8->FileStack[it8->IncludeSP]->FileName, + FileNest->FileName, cmsMAX_PATH-1) == FALSE) { + SynError(it8, "File path too long"); + return; + } + + FileNest->Stream = fopen(FileNest->FileName, "rt"); + if (FileNest->Stream == NULL) { + + SynError(it8, "File %s not found", FileNest->FileName); + return; + } + it8->IncludeSP++; + + it8 ->ch = ' '; + InSymbol(it8); + } + +} + +// Checks end of line separator +static +cmsBool CheckEOLN(cmsIT8* it8) +{ + if (!Check(it8, SEOLN, "Expected separator")) return FALSE; + while (it8 -> sy == SEOLN) + InSymbol(it8); + return TRUE; + +} + +// Skip a symbol + +static +void Skip(cmsIT8* it8, SYMBOL sy) +{ + if (it8->sy == sy && it8->sy != SEOF) + InSymbol(it8); +} + + +// Skip multiple EOLN +static +void SkipEOLN(cmsIT8* it8) +{ + while (it8->sy == SEOLN) { + InSymbol(it8); + } +} + + +// Returns a string holding current value +static +cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle) +{ + switch (it8->sy) { + + case SEOLN: // Empty value + Buffer[0]=0; + break; + case SIDENT: strncpy(Buffer, it8->id, max); + Buffer[max-1]=0; + break; + case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break; + case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break; + case SSTRING: strncpy(Buffer, it8->str, max); + Buffer[max-1] = 0; + break; + + + default: + return SynError(it8, "%s", ErrorTitle); + } + + Buffer[max] = 0; + return TRUE; +} + +// ---------------------------------------------------------- Table + +static +TABLE* GetTable(cmsIT8* it8) +{ + if ((it8 -> nTable >= it8 ->TablesCount)) { + + SynError(it8, "Table %d out of sequence", it8 -> nTable); + return it8 -> Tab; + } + + return it8 ->Tab + it8 ->nTable; +} + +// ---------------------------------------------------------- Memory management + + +// Frees an allocator and owned memory +void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + if (it8 == NULL) + return; + + if (it8->MemorySink) { + + OWNEDMEM* p; + OWNEDMEM* n; + + for (p = it8->MemorySink; p != NULL; p = n) { + + n = p->Next; + if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr); + _cmsFree(it8 ->ContextID, p); + } + } + + if (it8->MemoryBlock) + _cmsFree(it8 ->ContextID, it8->MemoryBlock); + + _cmsFree(it8 ->ContextID, it8); +} + + +// Allocates a chunk of data, keep linked list +static +void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size) +{ + OWNEDMEM* ptr1; + void* ptr = _cmsMallocZero(it8->ContextID, size); + + if (ptr != NULL) { + + ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM)); + + if (ptr1 == NULL) { + + _cmsFree(it8 ->ContextID, ptr); + return NULL; + } + + ptr1-> Ptr = ptr; + ptr1-> Next = it8 -> MemorySink; + it8 -> MemorySink = ptr1; + } + + return ptr; +} + + +// Suballocator. +static +void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) +{ + cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used; + cmsUInt8Number* ptr; + + size = _cmsALIGNMEM(size); + + if (size > Free) { + + if (it8 -> Allocator.BlockSize == 0) + + it8 -> Allocator.BlockSize = 20*1024; + else + it8 ->Allocator.BlockSize *= 2; + + if (it8 ->Allocator.BlockSize < size) + it8 ->Allocator.BlockSize = size; + + it8 ->Allocator.Used = 0; + it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize); + } + + ptr = it8 ->Allocator.Block + it8 ->Allocator.Used; + it8 ->Allocator.Used += size; + + return (void*) ptr; + +} + + +// Allocates a string +static +char *AllocString(cmsIT8* it8, const char* str) +{ + cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1; + char *ptr; + + + ptr = (char *) AllocChunk(it8, Size); + if (ptr) strncpy (ptr, str, Size-1); + + return ptr; +} + +// Searches through linked list + +static +cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr) +{ + if (LastPtr) *LastPtr = p; + + for (; p != NULL; p = p->Next) { + + if (LastPtr) *LastPtr = p; + + if (*Key != '#') { // Comments are ignored + + if (cmsstrcasecmp(Key, p->Keyword) == 0) + break; + } + } + + if (p == NULL) + return FALSE; + + if (Subkey == 0) + return TRUE; + + for (; p != NULL; p = p->NextSubkey) { + + if (p ->Subkey == NULL) continue; + + if (LastPtr) *LastPtr = p; + + if (cmsstrcasecmp(Subkey, p->Subkey) == 0) + return TRUE; + } + + return FALSE; +} + + + +// Add a property into a linked list +static +KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs) +{ + KEYVALUE* p; + KEYVALUE* last; + + + // Check if property is already in list + + if (IsAvailableOnList(*Head, Key, Subkey, &p)) { + + // This may work for editing properties + + // return SynError(it8, "duplicate key <%s>", Key); + } + else { + + last = p; + + // Allocate the container + p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE)); + if (p == NULL) + { + SynError(it8, "AddToList: out of memory"); + return NULL; + } + + // Store name and value + p->Keyword = AllocString(it8, Key); + p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey); + + // Keep the container in our list + if (*Head == NULL) { + *Head = p; + } + else + { + if (Subkey != NULL && last != NULL) { + + last->NextSubkey = p; + + // If Subkey is not null, then last is the last property with the same key, + // but not necessarily is the last property in the list, so we need to move + // to the actual list end + while (last->Next != NULL) + last = last->Next; + } + + if (last != NULL) last->Next = p; + } + + p->Next = NULL; + p->NextSubkey = NULL; + } + + p->WriteAs = WriteAs; + + if (xValue != NULL) { + + p->Value = AllocString(it8, xValue); + } + else { + p->Value = NULL; + } + + return p; +} + +static +KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as) +{ + return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as); +} + + +static +KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key) +{ + return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED); +} + + +static +void AllocTable(cmsIT8* it8) +{ + TABLE* t; + + t = it8 ->Tab + it8 ->TablesCount; + + t->HeaderList = NULL; + t->DataFormat = NULL; + t->Data = NULL; + + it8 ->TablesCount++; +} + + +cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable) +{ + cmsIT8* it8 = (cmsIT8*) IT8; + + if (nTable >= it8 ->TablesCount) { + + if (nTable == it8 ->TablesCount) { + + AllocTable(it8); + } + else { + SynError(it8, "Table %d is out of sequence", nTable); + return -1; + } + } + + it8 ->nTable = nTable; + + return (cmsInt32Number) nTable; +} + + + +// Init an empty container +cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID) +{ + cmsIT8* it8; + cmsUInt32Number i; + + it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8)); + if (it8 == NULL) return NULL; + + AllocTable(it8); + + it8->MemoryBlock = NULL; + it8->MemorySink = NULL; + + it8 ->nTable = 0; + + it8->ContextID = ContextID; + it8->Allocator.Used = 0; + it8->Allocator.Block = NULL; + it8->Allocator.BlockSize = 0; + + it8->ValidKeywords = NULL; + it8->ValidSampleID = NULL; + + it8 -> sy = SUNDEFINED; + it8 -> ch = ' '; + it8 -> Source = NULL; + it8 -> inum = 0; + it8 -> dnum = 0.0; + + it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); + it8->IncludeSP = 0; + it8 -> lineno = 1; + + strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); + cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17"); + + // Initialize predefined properties & data + + for (i=0; i < NUMPREDEFINEDPROPS; i++) + AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as); + + for (i=0; i < NUMPREDEFINEDSAMPLEID; i++) + AddAvailableSampleID(it8, PredefinedSampleID[i]); + + + return (cmsHANDLE) it8; +} + + +const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8) +{ + return GetTable((cmsIT8*) hIT8)->SheetType; +} + +cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type) +{ + TABLE* t = GetTable((cmsIT8*) hIT8); + + strncpy(t ->SheetType, Type, MAXSTR-1); + t ->SheetType[MAXSTR-1] = 0; + return TRUE; +} + +cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + if (!Val) return FALSE; + if (!*Val) return FALSE; + + return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL; +} + +// Sets a property +cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + if (!Val) return FALSE; + if (!*Val) return FALSE; + + return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buffer[1024]; + + snprintf(Buffer, 1023, it8->DoubleFormatter, Val); + + return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buffer[1024]; + + snprintf(Buffer, 1023, "%u", Val); + + return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL; +} + +// Gets a property +const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + + if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p)) + { + return p -> Value; + } + return NULL; +} + + +cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp) +{ + const char *v = cmsIT8GetProperty(hIT8, cProp); + + if (v == NULL) return 0.0; + + return ParseFloatNumber(v); +} + +const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + + if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) { + return p -> Value; + } + return NULL; +} + +// ----------------------------------------------------------------- Datasets + + +static +void AllocateDataFormat(cmsIT8* it8) +{ + TABLE* t = GetTable(it8); + + if (t -> DataFormat) return; // Already allocated + + t -> nSamples = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS"); + + if (t -> nSamples <= 0) { + + SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS"); + t -> nSamples = 10; + } + + t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *)); + if (t->DataFormat == NULL) { + + SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array"); + } + +} + +static +const char *GetDataFormat(cmsIT8* it8, int n) +{ + TABLE* t = GetTable(it8); + + if (t->DataFormat) + return t->DataFormat[n]; + + return NULL; +} + +static +cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) +{ + TABLE* t = GetTable(it8); + + if (!t->DataFormat) + AllocateDataFormat(it8); + + if (n > t -> nSamples) { + SynError(it8, "More than NUMBER_OF_FIELDS fields."); + return FALSE; + } + + if (t->DataFormat) { + t->DataFormat[n] = AllocString(it8, label); + } + + return TRUE; +} + + +cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample) +{ + cmsIT8* it8 = (cmsIT8*)h; + return SetDataFormat(it8, n, Sample); +} + +// A safe atoi that returns 0 when NULL input is given +static +cmsInt32Number satoi(const char* b) +{ + if (b == NULL) return 0; + return atoi(b); +} + +static +void AllocateDataSet(cmsIT8* it8) +{ + TABLE* t = GetTable(it8); + + if (t -> Data) return; // Already allocated + + t-> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + t-> nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); + + if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe) + { + SynError(it8, "AllocateDataSet: too much data"); + } + else { + // Some dumb analizers warns of possible overflow here, just take a look couple of lines above. + t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*)); + if (t->Data == NULL) { + + SynError(it8, "AllocateDataSet: Unable to allocate data array"); + } + } + +} + +static +char* GetData(cmsIT8* it8, int nSet, int nField) +{ + TABLE* t = GetTable(it8); + int nSamples = t -> nSamples; + int nPatches = t -> nPatches; + + if (nSet >= nPatches || nField >= nSamples) + return NULL; + + if (!t->Data) return NULL; + return t->Data [nSet * nSamples + nField]; +} + +static +cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val) +{ + TABLE* t = GetTable(it8); + + if (!t->Data) + AllocateDataSet(it8); + + if (!t->Data) return FALSE; + + if (nSet > t -> nPatches || nSet < 0) { + + return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches); + } + + if (nField > t ->nSamples || nField < 0) { + return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples); + + } + + t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val); + return TRUE; +} + + +// --------------------------------------------------------------- File I/O + + +// Writes a string to file +static +void WriteStr(SAVESTREAM* f, const char *str) +{ + cmsUInt32Number len; + + if (str == NULL) + str = " "; + + // Length to write + len = (cmsUInt32Number) strlen(str); + f ->Used += len; + + + if (f ->stream) { // Should I write it to a file? + + if (fwrite(str, 1, len, f->stream) != len) { + cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser"); + return; + } + + } + else { // Or to a memory block? + + if (f ->Base) { // Am I just counting the bytes? + + if (f ->Used > f ->Max) { + + cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser"); + return; + } + + memmove(f ->Ptr, str, len); + f->Ptr += len; + } + + } +} + + +// Write formatted + +static +void Writef(SAVESTREAM* f, const char* frm, ...) +{ + char Buffer[4096]; + va_list args; + + va_start(args, frm); + vsnprintf(Buffer, 4095, frm, args); + Buffer[4095] = 0; + WriteStr(f, Buffer); + va_end(args); + +} + +// Writes full header +static +void WriteHeader(cmsIT8* it8, SAVESTREAM* fp) +{ + KEYVALUE* p; + TABLE* t = GetTable(it8); + + // Writes the type + WriteStr(fp, t->SheetType); + WriteStr(fp, "\n"); + + for (p = t->HeaderList; (p != NULL); p = p->Next) + { + if (*p ->Keyword == '#') { + + char* Pt; + + WriteStr(fp, "#\n# "); + for (Pt = p ->Value; *Pt; Pt++) { + + + Writef(fp, "%c", *Pt); + + if (*Pt == '\n') { + WriteStr(fp, "# "); + } + } + + WriteStr(fp, "\n#\n"); + continue; + } + + + if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) { + +#ifdef CMS_STRICT_CGATS + WriteStr(fp, "KEYWORD\t\""); + WriteStr(fp, p->Keyword); + WriteStr(fp, "\"\n"); +#endif + + AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED); + } + + WriteStr(fp, p->Keyword); + if (p->Value) { + + switch (p ->WriteAs) { + + case WRITE_UNCOOKED: + Writef(fp, "\t%s", p ->Value); + break; + + case WRITE_STRINGIFY: + Writef(fp, "\t\"%s\"", p->Value ); + break; + + case WRITE_HEXADECIMAL: + Writef(fp, "\t0x%X", satoi(p ->Value)); + break; + + case WRITE_BINARY: + Writef(fp, "\t0x%B", satoi(p ->Value)); + break; + + case WRITE_PAIR: + Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value); + break; + + default: SynError(it8, "Unknown write mode %d", p ->WriteAs); + return; + } + } + + WriteStr (fp, "\n"); + } + +} + + +// Writes the data format +static +void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8) +{ + int i, nSamples; + TABLE* t = GetTable(it8); + + if (!t -> DataFormat) return; + + WriteStr(fp, "BEGIN_DATA_FORMAT\n"); + WriteStr(fp, " "); + nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + + for (i = 0; i < nSamples; i++) { + + WriteStr(fp, t->DataFormat[i]); + WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t")); + } + + WriteStr (fp, "END_DATA_FORMAT\n"); +} + + +// Writes data array +static +void WriteData(SAVESTREAM* fp, cmsIT8* it8) +{ + int i, j; + TABLE* t = GetTable(it8); + + if (!t->Data) return; + + WriteStr (fp, "BEGIN_DATA\n"); + + t->nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); + + for (i = 0; i < t-> nPatches; i++) { + + WriteStr(fp, " "); + + for (j = 0; j < t->nSamples; j++) { + + char *ptr = t->Data[i*t->nSamples+j]; + + if (ptr == NULL) WriteStr(fp, "\"\""); + else { + // If value contains whitespace, enclose within quote + + if (strchr(ptr, ' ') != NULL) { + + WriteStr(fp, "\""); + WriteStr(fp, ptr); + WriteStr(fp, "\""); + } + else + WriteStr(fp, ptr); + } + + WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t")); + } + } + WriteStr (fp, "END_DATA\n"); +} + + + +// Saves whole file +cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName) +{ + SAVESTREAM sd; + cmsUInt32Number i; + cmsIT8* it8 = (cmsIT8*) hIT8; + + memset(&sd, 0, sizeof(sd)); + + sd.stream = fopen(cFileName, "wt"); + if (!sd.stream) return FALSE; + + for (i=0; i < it8 ->TablesCount; i++) { + + cmsIT8SetTable(hIT8, i); + WriteHeader(it8, &sd); + WriteDataFormat(&sd, it8); + WriteData(&sd, it8); + } + + if (fclose(sd.stream) != 0) return FALSE; + + return TRUE; +} + + +// Saves to memory +cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded) +{ + SAVESTREAM sd; + cmsUInt32Number i; + cmsIT8* it8 = (cmsIT8*) hIT8; + + memset(&sd, 0, sizeof(sd)); + + sd.stream = NULL; + sd.Base = (cmsUInt8Number*) MemPtr; + sd.Ptr = sd.Base; + + sd.Used = 0; + + if (sd.Base) + sd.Max = *BytesNeeded; // Write to memory? + else + sd.Max = 0; // Just counting the needed bytes + + for (i=0; i < it8 ->TablesCount; i++) { + + cmsIT8SetTable(hIT8, i); + WriteHeader(it8, &sd); + WriteDataFormat(&sd, it8); + WriteData(&sd, it8); + } + + sd.Used++; // The \0 at the very end + + if (sd.Base) + *sd.Ptr = 0; + + *BytesNeeded = sd.Used; + + return TRUE; +} + + +// -------------------------------------------------------------- Higher level parsing + +static +cmsBool DataFormatSection(cmsIT8* it8) +{ + int iField = 0; + TABLE* t = GetTable(it8); + + InSymbol(it8); // Eats "BEGIN_DATA_FORMAT" + CheckEOLN(it8); + + while (it8->sy != SEND_DATA_FORMAT && + it8->sy != SEOLN && + it8->sy != SEOF && + it8->sy != SSYNERROR) { + + if (it8->sy != SIDENT) { + + return SynError(it8, "Sample type expected"); + } + + if (!SetDataFormat(it8, iField, it8->id)) return FALSE; + iField++; + + InSymbol(it8); + SkipEOLN(it8); + } + + SkipEOLN(it8); + Skip(it8, SEND_DATA_FORMAT); + SkipEOLN(it8); + + if (iField != t ->nSamples) { + SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField); + + + } + + return TRUE; +} + + + +static +cmsBool DataSection (cmsIT8* it8) +{ + int iField = 0; + int iSet = 0; + char Buffer[256]; + TABLE* t = GetTable(it8); + + InSymbol(it8); // Eats "BEGIN_DATA" + CheckEOLN(it8); + + if (!t->Data) + AllocateDataSet(it8); + + while (it8->sy != SEND_DATA && it8->sy != SEOF) + { + if (iField >= t -> nSamples) { + iField = 0; + iSet++; + + } + + if (it8->sy != SEND_DATA && it8->sy != SEOF) { + + if (!GetVal(it8, Buffer, 255, "Sample data expected")) + return FALSE; + + if (!SetData(it8, iSet, iField, Buffer)) + return FALSE; + + iField++; + + InSymbol(it8); + SkipEOLN(it8); + } + } + + SkipEOLN(it8); + Skip(it8, SEND_DATA); + SkipEOLN(it8); + + // Check for data completion. + + if ((iSet+1) != t -> nPatches) + return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1); + + return TRUE; +} + + + + +static +cmsBool HeaderSection(cmsIT8* it8) +{ + char VarName[MAXID]; + char Buffer[MAXSTR]; + KEYVALUE* Key; + + while (it8->sy != SEOF && + it8->sy != SSYNERROR && + it8->sy != SBEGIN_DATA_FORMAT && + it8->sy != SBEGIN_DATA) { + + + switch (it8 -> sy) { + + case SKEYWORD: + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE; + if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE; + InSymbol(it8); + break; + + + case SDATA_FORMAT_ID: + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE; + if (!AddAvailableSampleID(it8, Buffer)) return FALSE; + InSymbol(it8); + break; + + + case SIDENT: + strncpy(VarName, it8->id, MAXID - 1); + VarName[MAXID - 1] = 0; + + if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) { + +#ifdef CMS_STRICT_CGATS + return SynError(it8, "Undefined keyword '%s'", VarName); +#else + Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED); + if (Key == NULL) return FALSE; +#endif + } + + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE; + + if (Key->WriteAs != WRITE_PAIR) { + AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer, + (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED); + } + else { + const char *Subkey; + char *Nextkey; + if (it8->sy != SSTRING) + return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName); + + // chop the string as a list of "subkey, value" pairs, using ';' as a separator + for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey) + { + char *Value, *temp; + + // identify token pair boundary + Nextkey = (char*)strchr(Subkey, ';'); + if (Nextkey) + *Nextkey++ = '\0'; + + // for each pair, split the subkey and the value + Value = (char*)strrchr(Subkey, ','); + if (Value == NULL) + return SynError(it8, "Invalid value for property '%s'.", VarName); + + // gobble the spaces before the coma, and the coma itself + temp = Value++; + do *temp-- = '\0'; while (temp >= Subkey && *temp == ' '); + + // gobble any space at the right + temp = Value + strlen(Value) - 1; + while (*temp == ' ') *temp-- = '\0'; + + // trim the strings from the left + Subkey += strspn(Subkey, " "); + Value += strspn(Value, " "); + + if (Subkey[0] == 0 || Value[0] == 0) + return SynError(it8, "Invalid value for property '%s'.", VarName); + AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR); + } + } + + InSymbol(it8); + break; + + + case SEOLN: break; + + default: + return SynError(it8, "expected keyword or identifier"); + } + + SkipEOLN(it8); + } + + return TRUE; + +} + + +static +void ReadType(cmsIT8* it8, char* SheetTypePtr) +{ + cmsInt32Number cnt = 0; + + // First line is a very special case. + + while (isseparator(it8->ch)) + NextCh(it8); + + while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != 0) { + + if (cnt++ < MAXSTR) + *SheetTypePtr++= (char) it8 ->ch; + NextCh(it8); + } + + *SheetTypePtr = 0; +} + + +static +cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet) +{ + char* SheetTypePtr = it8 ->Tab[0].SheetType; + + if (nosheet == 0) { + ReadType(it8, SheetTypePtr); + } + + InSymbol(it8); + + SkipEOLN(it8); + + while (it8-> sy != SEOF && + it8-> sy != SSYNERROR) { + + switch (it8 -> sy) { + + case SBEGIN_DATA_FORMAT: + if (!DataFormatSection(it8)) return FALSE; + break; + + case SBEGIN_DATA: + + if (!DataSection(it8)) return FALSE; + + if (it8 -> sy != SEOF) { + + AllocTable(it8); + it8 ->nTable = it8 ->TablesCount - 1; + + // Read sheet type if present. We only support identifier and string. + // is a type string + // anything else, is not a type string + if (nosheet == 0) { + + if (it8 ->sy == SIDENT) { + + // May be a type sheet or may be a prop value statement. We cannot use insymbol in + // this special case... + while (isseparator(it8->ch)) + NextCh(it8); + + // If a newline is found, then this is a type string + if (it8 ->ch == '\n' || it8->ch == '\r') { + + cmsIT8SetSheetType(it8, it8 ->id); + InSymbol(it8); + } + else + { + // It is not. Just continue + cmsIT8SetSheetType(it8, ""); + } + } + else + // Validate quoted strings + if (it8 ->sy == SSTRING) { + cmsIT8SetSheetType(it8, it8 ->str); + InSymbol(it8); + } + } + + } + break; + + case SEOLN: + SkipEOLN(it8); + break; + + default: + if (!HeaderSection(it8)) return FALSE; + } + + } + + return (it8 -> sy != SSYNERROR); +} + + + +// Init useful pointers + +static +void CookPointers(cmsIT8* it8) +{ + int idField, i; + char* Fld; + cmsUInt32Number j; + cmsUInt32Number nOldTable = it8 ->nTable; + + for (j=0; j < it8 ->TablesCount; j++) { + + TABLE* t = it8 ->Tab + j; + + t -> SampleID = 0; + it8 ->nTable = j; + + for (idField = 0; idField < t -> nSamples; idField++) + { + if (t ->DataFormat == NULL){ + SynError(it8, "Undefined DATA_FORMAT"); + return; + } + + Fld = t->DataFormat[idField]; + if (!Fld) continue; + + + if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) { + + t -> SampleID = idField; + } + + // "LABEL" is an extension. It keeps references to forward tables + + if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') { + + // Search for table references... + for (i = 0; i < t->nPatches; i++) { + + char* Label = GetData(it8, i, idField); + + if (Label) { + + cmsUInt32Number k; + + // This is the label, search for a table containing + // this property + + for (k = 0; k < it8->TablesCount; k++) { + + TABLE* Table = it8->Tab + k; + KEYVALUE* p; + + if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) { + + // Available, keep type and table + char Buffer[256]; + + char* Type = p->Value; + int nTable = (int)k; + + snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type); + + SetData(it8, i, idField, Buffer); + } + } + + + } + + } + + + } + + } + } + + it8 ->nTable = nOldTable; +} + +// Try to infere if the file is a CGATS/IT8 file at all. Read first line +// that should be something like some printable characters plus a \n +// returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line? +static +int IsMyBlock(const cmsUInt8Number* Buffer, cmsUInt32Number n) +{ + int words = 1, space = 0, quot = 0; + cmsUInt32Number i; + + if (n < 10) return 0; // Too small + + if (n > 132) + n = 132; + + for (i = 1; i < n; i++) { + + switch(Buffer[i]) + { + case '\n': + case '\r': + return ((quot == 1) || (words > 2)) ? 0 : words; + case '\t': + case ' ': + if(!quot && !space) + space = 1; + break; + case '\"': + quot = !quot; + break; + default: + if (Buffer[i] < 32) return 0; + if (Buffer[i] > 127) return 0; + words += space; + space = 0; + break; + } + } + + return 0; +} + + +static +cmsBool IsMyFile(const char* FileName) +{ + FILE *fp; + cmsUInt32Number Size; + cmsUInt8Number Ptr[133]; + + fp = fopen(FileName, "rt"); + if (!fp) { + cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName); + return FALSE; + } + + Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp); + + if (fclose(fp) != 0) + return FALSE; + + Ptr[Size] = '\0'; + + return IsMyBlock(Ptr, Size); +} + +// ---------------------------------------------------------- Exported routines + + +cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len) +{ + cmsHANDLE hIT8; + cmsIT8* it8; + int type; + + _cmsAssert(Ptr != NULL); + _cmsAssert(len != 0); + + type = IsMyBlock((const cmsUInt8Number*)Ptr, len); + if (type == 0) return NULL; + + hIT8 = cmsIT8Alloc(ContextID); + if (!hIT8) return NULL; + + it8 = (cmsIT8*) hIT8; + it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1); + if (it8->MemoryBlock == NULL) + { + cmsIT8Free(hIT8); + return FALSE; + } + + strncpy(it8 ->MemoryBlock, (const char*) Ptr, len); + it8 ->MemoryBlock[len] = 0; + + strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1); + it8-> Source = it8 -> MemoryBlock; + + if (!ParseIT8(it8, type-1)) { + + cmsIT8Free(hIT8); + return FALSE; + } + + CookPointers(it8); + it8 ->nTable = 0; + + _cmsFree(ContextID, it8->MemoryBlock); + it8 -> MemoryBlock = NULL; + + return hIT8; + + +} + + +cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName) +{ + + cmsHANDLE hIT8; + cmsIT8* it8; + int type; + + _cmsAssert(cFileName != NULL); + + type = IsMyFile(cFileName); + if (type == 0) return NULL; + + hIT8 = cmsIT8Alloc(ContextID); + it8 = (cmsIT8*) hIT8; + if (!hIT8) return NULL; + + + it8 ->FileStack[0]->Stream = fopen(cFileName, "rt"); + + if (!it8 ->FileStack[0]->Stream) { + cmsIT8Free(hIT8); + return NULL; + } + + + strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1); + it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0; + + if (!ParseIT8(it8, type-1)) { + + fclose(it8 ->FileStack[0]->Stream); + cmsIT8Free(hIT8); + return NULL; + } + + CookPointers(it8); + it8 ->nTable = 0; + + if (fclose(it8 ->FileStack[0]->Stream)!= 0) { + cmsIT8Free(hIT8); + return NULL; + } + + return hIT8; + +} + +int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + if (SampleNames) + *SampleNames = t -> DataFormat; + return t -> nSamples; +} + + +cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + cmsUInt32Number n; + char **Props; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + // Pass#1 - count properties + + n = 0; + for (p = t -> HeaderList; p != NULL; p = p->Next) { + n++; + } + + + Props = (char **) AllocChunk(it8, sizeof(char *) * n); + + // Pass#2 - Fill pointers + n = 0; + for (p = t -> HeaderList; p != NULL; p = p->Next) { + Props[n++] = p -> Keyword; + } + + *PropertyNames = Props; + return n; +} + +cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE *p, *tmp; + cmsUInt32Number n; + const char **Props; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + + t = GetTable(it8); + + if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) { + *SubpropertyNames = 0; + return 0; + } + + // Pass#1 - count properties + + n = 0; + for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { + if(tmp->Subkey != NULL) + n++; + } + + + Props = (const char **) AllocChunk(it8, sizeof(char *) * n); + + // Pass#2 - Fill pointers + n = 0; + for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { + if(tmp->Subkey != NULL) + Props[n++] = p ->Subkey; + } + + *SubpropertyNames = Props; + return n; +} + +static +int LocatePatch(cmsIT8* it8, const char* cPatch) +{ + int i; + const char *data; + TABLE* t = GetTable(it8); + + for (i=0; i < t-> nPatches; i++) { + + data = GetData(it8, i, t->SampleID); + + if (data != NULL) { + + if (cmsstrcasecmp(data, cPatch) == 0) + return i; + } + } + + // SynError(it8, "Couldn't find patch '%s'\n", cPatch); + return -1; +} + + +static +int LocateEmptyPatch(cmsIT8* it8) +{ + int i; + const char *data; + TABLE* t = GetTable(it8); + + for (i=0; i < t-> nPatches; i++) { + + data = GetData(it8, i, t->SampleID); + + if (data == NULL) + return i; + + } + + return -1; +} + +static +int LocateSample(cmsIT8* it8, const char* cSample) +{ + int i; + const char *fld; + TABLE* t = GetTable(it8); + + for (i=0; i < t->nSamples; i++) { + + fld = GetDataFormat(it8, i); + if (fld != NULL) { + if (cmsstrcasecmp(fld, cSample) == 0) + return i; + } + } + + return -1; + +} + + +int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return LocateSample(it8, cSample); +} + + + +const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return GetData(it8, row, col); +} + + +cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col) +{ + const char* Buffer; + + Buffer = cmsIT8GetDataRowCol(hIT8, row, col); + + if (Buffer == NULL) return 0.0; + + return ParseFloatNumber(Buffer); +} + + +cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return SetData(it8, row, col, Val); +} + + +cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buff[256]; + + _cmsAssert(hIT8 != NULL); + + snprintf(Buff, 255, it8->DoubleFormatter, Val); + + return SetData(it8, row, col, Buff); +} + + + +const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + int iField, iSet; + + _cmsAssert(hIT8 != NULL); + + iField = LocateSample(it8, cSample); + if (iField < 0) { + return NULL; + } + + iSet = LocatePatch(it8, cPatch); + if (iSet < 0) { + return NULL; + } + + return GetData(it8, iSet, iField); +} + + +cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample) +{ + const char* Buffer; + + Buffer = cmsIT8GetData(it8, cPatch, cSample); + + return ParseFloatNumber(Buffer); +} + + + +cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + int iField, iSet; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + iField = LocateSample(it8, cSample); + + if (iField < 0) + return FALSE; + + if (t-> nPatches == 0) { + + AllocateDataFormat(it8); + AllocateDataSet(it8); + CookPointers(it8); + } + + if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) { + + iSet = LocateEmptyPatch(it8); + if (iSet < 0) { + return SynError(it8, "Couldn't add more patches '%s'\n", cPatch); + } + + iField = t -> SampleID; + } + else { + iSet = LocatePatch(it8, cPatch); + if (iSet < 0) { + return FALSE; + } + } + + return SetData(it8, iSet, iField, Val); +} + + +cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + cmsFloat64Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buff[256]; + + _cmsAssert(hIT8 != NULL); + + snprintf(Buff, 255, it8->DoubleFormatter, Val); + return cmsIT8SetData(hIT8, cPatch, cSample, Buff); +} + +// Buffer should get MAXSTR at least + +const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + TABLE* t; + char* Data; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + Data = GetData(it8, nPatch, t->SampleID); + + if (!Data) return NULL; + if (!buffer) return Data; + + strncpy(buffer, Data, MAXSTR-1); + buffer[MAXSTR-1] = 0; + return buffer; +} + +int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch) +{ + _cmsAssert(hIT8 != NULL); + + return LocatePatch((cmsIT8*)hIT8, cPatch); +} + +cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return it8 ->TablesCount; +} + +// This handles the "LABEL" extension. +// Label, nTable, Type + +int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType) +{ + const char* cLabelFld; + char Type[256], Label[256]; + cmsUInt32Number nTable; + + _cmsAssert(hIT8 != NULL); + + if (cField != NULL && *cField == 0) + cField = "LABEL"; + + if (cField == NULL) + cField = "LABEL"; + + cLabelFld = cmsIT8GetData(hIT8, cSet, cField); + if (!cLabelFld) return -1; + + if (sscanf(cLabelFld, "%255s %u %255s", Label, &nTable, Type) != 3) + return -1; + + if (ExpectedType != NULL && *ExpectedType == 0) + ExpectedType = NULL; + + if (ExpectedType) { + + if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1; + } + + return cmsIT8SetTable(hIT8, nTable); +} + + +cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + int pos; + + _cmsAssert(hIT8 != NULL); + + pos = LocateSample(it8, cSample); + if(pos == -1) + return FALSE; + + it8->Tab[it8->nTable].SampleID = pos; + return TRUE; +} + + +void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + if (Formatter == NULL) + strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); + else + strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter)); + + it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0; +} + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscnvrt.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscnvrt.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscnvrt.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscnvrt.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1243 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// This is the default routine for ICC-style intents. A user may decide to override it by using a plugin. +// Supported intents are perceptual, relative colorimetric, saturation and ICC-absolute colorimetric +static +cmsPipeline* DefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + +// This is the entry for black-preserving K-only intents, which are non-ICC. Last profile have to be a output profile +// to do the trick (no devicelinks allowed at that position) +static +cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + +// This is the entry for black-plane preserving, which are non-ICC. Again, Last profile have to be a output profile +// to do the trick (no devicelinks allowed at that position) +static +cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + + +// This is a structure holding implementations for all supported intents. +typedef struct _cms_intents_list { + + cmsUInt32Number Intent; + char Description[256]; + cmsIntentFn Link; + struct _cms_intents_list* Next; + +} cmsIntentsList; + + +// Built-in intents +static cmsIntentsList DefaultIntents[] = { + + { INTENT_PERCEPTUAL, "Perceptual", DefaultICCintents, &DefaultIntents[1] }, + { INTENT_RELATIVE_COLORIMETRIC, "Relative colorimetric", DefaultICCintents, &DefaultIntents[2] }, + { INTENT_SATURATION, "Saturation", DefaultICCintents, &DefaultIntents[3] }, + { INTENT_ABSOLUTE_COLORIMETRIC, "Absolute colorimetric", DefaultICCintents, &DefaultIntents[4] }, + { INTENT_PRESERVE_K_ONLY_PERCEPTUAL, "Perceptual preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[5] }, + { INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC, "Relative colorimetric preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[6] }, + { INTENT_PRESERVE_K_ONLY_SATURATION, "Saturation preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[7] }, + { INTENT_PRESERVE_K_PLANE_PERCEPTUAL, "Perceptual preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[8] }, + { INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC,"Relative colorimetric preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[9] }, + { INTENT_PRESERVE_K_PLANE_SATURATION, "Saturation preserving black plane", BlackPreservingKPlaneIntents, NULL } +}; + + +// A pointer to the beginning of the list +_cmsIntentsPluginChunkType _cmsIntentsPluginChunk = { NULL }; + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginIntentsList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsIntentsPluginChunkType newHead = { NULL }; + cmsIntentsList* entry; + cmsIntentsList* Anterior = NULL; + _cmsIntentsPluginChunkType* head = (_cmsIntentsPluginChunkType*) src->chunks[IntentPlugin]; + + // Walk the list copying all nodes + for (entry = head->Intents; + entry != NULL; + entry = entry ->Next) { + + cmsIntentsList *newEntry = ( cmsIntentsList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsIntentsList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.Intents == NULL) + newHead.Intents = newEntry; + } + + ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsIntentsPluginChunkType)); +} + +void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Copy all linked list + DupPluginIntentsList(ctx, src); + } + else { + static _cmsIntentsPluginChunkType IntentsPluginChunkType = { NULL }; + ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx ->MemPool, &IntentsPluginChunkType, sizeof(_cmsIntentsPluginChunkType)); + } +} + + +// Search the list for a suitable intent. Returns NULL if not found +static +cmsIntentsList* SearchIntent(cmsContext ContextID, cmsUInt32Number Intent) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); + cmsIntentsList* pt; + + for (pt = ctx -> Intents; pt != NULL; pt = pt -> Next) + if (pt ->Intent == Intent) return pt; + + for (pt = DefaultIntents; pt != NULL; pt = pt -> Next) + if (pt ->Intent == Intent) return pt; + + return NULL; +} + +// Black point compensation. Implemented as a linear scaling in XYZ. Black points +// should come relative to the white point. Fills an matrix/offset element m +// which is organized as a 4x4 matrix. +static +void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn, + const cmsCIEXYZ* BlackPointOut, + cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number ax, ay, az, bx, by, bz, tx, ty, tz; + + // Now we need to compute a matrix plus an offset m and of such of + // [m]*bpin + off = bpout + // [m]*D50 + off = D50 + // + // This is a linear scaling in the form ax+b, where + // a = (bpout - D50) / (bpin - D50) + // b = - D50* (bpout - bpin) / (bpin - D50) + + tx = BlackPointIn->X - cmsD50_XYZ()->X; + ty = BlackPointIn->Y - cmsD50_XYZ()->Y; + tz = BlackPointIn->Z - cmsD50_XYZ()->Z; + + ax = (BlackPointOut->X - cmsD50_XYZ()->X) / tx; + ay = (BlackPointOut->Y - cmsD50_XYZ()->Y) / ty; + az = (BlackPointOut->Z - cmsD50_XYZ()->Z) / tz; + + bx = - cmsD50_XYZ()-> X * (BlackPointOut->X - BlackPointIn->X) / tx; + by = - cmsD50_XYZ()-> Y * (BlackPointOut->Y - BlackPointIn->Y) / ty; + bz = - cmsD50_XYZ()-> Z * (BlackPointOut->Z - BlackPointIn->Z) / tz; + + _cmsVEC3init(&m ->v[0], ax, 0, 0); + _cmsVEC3init(&m ->v[1], 0, ay, 0); + _cmsVEC3init(&m ->v[2], 0, 0, az); + _cmsVEC3init(off, bx, by, bz); + +} + + +// Approximate a blackbody illuminant based on CHAD information +static +cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad) +{ + // Convert D50 across inverse CHAD to get the absolute white point + cmsVEC3 d, s; + cmsCIEXYZ Dest; + cmsCIExyY DestChromaticity; + cmsFloat64Number TempK; + cmsMAT3 m1, m2; + + m1 = *Chad; + if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; + + s.n[VX] = cmsD50_XYZ() -> X; + s.n[VY] = cmsD50_XYZ() -> Y; + s.n[VZ] = cmsD50_XYZ() -> Z; + + _cmsMAT3eval(&d, &m2, &s); + + Dest.X = d.n[VX]; + Dest.Y = d.n[VY]; + Dest.Z = d.n[VZ]; + + cmsXYZ2xyY(&DestChromaticity, &Dest); + + if (!cmsTempFromWhitePoint(&TempK, &DestChromaticity)) + return -1.0; + + return TempK; +} + +// Compute a CHAD based on a given temperature +static + void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp) +{ + cmsCIEXYZ White; + cmsCIExyY ChromaticityOfWhite; + + cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp); + cmsxyY2XYZ(&White, &ChromaticityOfWhite); + _cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ()); +} + +// Join scalings to obtain relative input to absolute and then to relative output. +// Result is stored in a 3x3 matrix +static +cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState, + const cmsCIEXYZ* WhitePointIn, + const cmsMAT3* ChromaticAdaptationMatrixIn, + const cmsCIEXYZ* WhitePointOut, + const cmsMAT3* ChromaticAdaptationMatrixOut, + cmsMAT3* m) +{ + cmsMAT3 Scale, m1, m2, m3, m4; + + // TODO: Follow Marc Mahy's recommendation to check if CHAD is same by using M1*M2 == M2*M1. If so, do nothing. + // TODO: Add support for ArgyllArts tag + + // Adaptation state + if (AdaptationState == 1.0) { + + // Observer is fully adapted. Keep chromatic adaptation. + // That is the standard V4 behaviour + _cmsVEC3init(&m->v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); + _cmsVEC3init(&m->v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); + _cmsVEC3init(&m->v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); + + } + else { + + // Incomplete adaptation. This is an advanced feature. + _cmsVEC3init(&Scale.v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); + _cmsVEC3init(&Scale.v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); + _cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); + + + if (AdaptationState == 0.0) { + + m1 = *ChromaticAdaptationMatrixOut; + _cmsMAT3per(&m2, &m1, &Scale); + // m2 holds CHAD from output white to D50 times abs. col. scaling + + // Observer is not adapted, undo the chromatic adaptation + _cmsMAT3per(m, &m2, ChromaticAdaptationMatrixOut); + + m3 = *ChromaticAdaptationMatrixIn; + if (!_cmsMAT3inverse(&m3, &m4)) return FALSE; + _cmsMAT3per(m, &m2, &m4); + + } else { + + cmsMAT3 MixedCHAD; + cmsFloat64Number TempSrc, TempDest, Temp; + + m1 = *ChromaticAdaptationMatrixIn; + if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; + _cmsMAT3per(&m3, &m2, &Scale); + // m3 holds CHAD from input white to D50 times abs. col. scaling + + TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn); + TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut); + + if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong + + if (_cmsMAT3isIdentity(&Scale) && fabs(TempSrc - TempDest) < 0.01) { + + _cmsMAT3identity(m); + return TRUE; + } + + Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc; + + // Get a CHAD from whatever output temperature to D50. This replaces output CHAD + Temp2CHAD(&MixedCHAD, Temp); + + _cmsMAT3per(m, &m3, &MixedCHAD); + } + + } + return TRUE; + +} + +// Just to see if m matrix should be applied +static +cmsBool IsEmptyLayer(cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number diff = 0; + cmsMAT3 Ident; + int i; + + if (m == NULL && off == NULL) return TRUE; // NULL is allowed as an empty layer + if (m == NULL && off != NULL) return FALSE; // This is an internal error + + _cmsMAT3identity(&Ident); + + for (i=0; i < 3*3; i++) + diff += fabs(((cmsFloat64Number*)m)[i] - ((cmsFloat64Number*)&Ident)[i]); + + for (i=0; i < 3; i++) + diff += fabs(((cmsFloat64Number*)off)[i]); + + + return (diff < 0.002); +} + + +// Compute the conversion layer +static +cmsBool ComputeConversion(cmsUInt32Number i, + cmsHPROFILE hProfiles[], + cmsUInt32Number Intent, + cmsBool BPC, + cmsFloat64Number AdaptationState, + cmsMAT3* m, cmsVEC3* off) +{ + + int k; + + // m and off are set to identity and this is detected latter on + _cmsMAT3identity(m); + _cmsVEC3init(off, 0, 0, 0); + + // If intent is abs. colorimetric, + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { + + cmsCIEXYZ WhitePointIn, WhitePointOut; + cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; + + _cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]); + _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]); + + _cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]); + _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]); + + if (!ComputeAbsoluteIntent(AdaptationState, + &WhitePointIn, &ChromaticAdaptationMatrixIn, + &WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE; + + } + else { + // Rest of intents may apply BPC. + + if (BPC) { + + cmsCIEXYZ BlackPointIn, BlackPointOut; + + cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); + cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0); + + // If black points are equal, then do nothing + if (BlackPointIn.X != BlackPointOut.X || + BlackPointIn.Y != BlackPointOut.Y || + BlackPointIn.Z != BlackPointOut.Z) + ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off); + } + } + + // Offset should be adjusted because the encoding. We encode XYZ normalized to 0..1.0, + // to do that, we divide by MAX_ENCODEABLE_XZY. The conversion stage goes XYZ -> XYZ so + // we have first to convert from encoded to XYZ and then convert back to encoded. + // y = Mx + Off + // x = x'c + // y = M x'c + Off + // y = y'c; y' = y / c + // y' = (Mx'c + Off) /c = Mx' + (Off / c) + + for (k=0; k < 3; k++) { + off ->n[k] /= MAX_ENCODEABLE_XYZ; + } + + return TRUE; +} + + +// Add a conversion stage if needed. If a matrix/offset m is given, it applies to XYZ space +static +cmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColorSpaceSignature OutPCS, cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number* m_as_dbl = (cmsFloat64Number*) m; + cmsFloat64Number* off_as_dbl = (cmsFloat64Number*) off; + + // Handle PCS mismatches. A specialized stage is added to the LUT in such case + switch (InPCS) { + + case cmsSigXYZData: // Input profile operates in XYZ + + switch (OutPCS) { + + case cmsSigXYZData: // XYZ -> XYZ + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + break; + + case cmsSigLabData: // XYZ -> Lab + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) + return FALSE; + break; + + default: + return FALSE; // Colorspace mismatch + } + break; + + case cmsSigLabData: // Input profile operates in Lab + + switch (OutPCS) { + + case cmsSigXYZData: // Lab -> XYZ + + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID))) + return FALSE; + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + break; + + case cmsSigLabData: // Lab -> Lab + + if (!IsEmptyLayer(m, off)) { + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)) || + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)) || + !cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) + return FALSE; + } + break; + + default: + return FALSE; // Mismatch + } + break; + + // On colorspaces other than PCS, check for same space + default: + if (InPCS != OutPCS) return FALSE; + break; + } + + return TRUE; +} + + +// Is a given space compatible with another? +static +cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature b) +{ + // If they are same, they are compatible. + if (a == b) return TRUE; + + // Check for MCH4 substitution of CMYK + if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE; + if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE; + + // Check for XYZ/Lab. Those spaces are interchangeable as they can be computed one from other. + if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE; + if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE; + + return FALSE; +} + + +// Default handler for ICC-style intents +static +cmsPipeline* DefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsPipeline* Lut = NULL; + cmsPipeline* Result; + cmsHPROFILE hProfile; + cmsMAT3 m; + cmsVEC3 off; + cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut = cmsSigLabData, CurrentColorSpace; + cmsProfileClassSignature ClassSig; + cmsUInt32Number i, Intent; + + // For safety + if (nProfiles == 0) return NULL; + + // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined' + Result = cmsPipelineAlloc(ContextID, 0, 0); + if (Result == NULL) return NULL; + + CurrentColorSpace = cmsGetColorSpace(hProfiles[0]); + + for (i=0; i < nProfiles; i++) { + + cmsBool lIsDeviceLink, lIsInput; + + hProfile = hProfiles[i]; + ClassSig = cmsGetDeviceClass(hProfile); + lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass ); + + // First profile is used as input unless devicelink or abstract + if ((i == 0) && !lIsDeviceLink) { + lIsInput = TRUE; + } + else { + // Else use profile in the input direction if current space is not PCS + lIsInput = (CurrentColorSpace != cmsSigXYZData) && + (CurrentColorSpace != cmsSigLabData); + } + + Intent = TheIntents[i]; + + if (lIsInput || lIsDeviceLink) { + + ColorSpaceIn = cmsGetColorSpace(hProfile); + ColorSpaceOut = cmsGetPCS(hProfile); + } + else { + + ColorSpaceIn = cmsGetPCS(hProfile); + ColorSpaceOut = cmsGetColorSpace(hProfile); + } + + if (!ColorSpaceIsCompatible(ColorSpaceIn, CurrentColorSpace)) { + + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "ColorSpace mismatch"); + goto Error; + } + + // If devicelink is found, then no custom intent is allowed and we can + // read the LUT to be applied. Settings don't apply here. + if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) { + + // Get the involved LUT from the profile + Lut = _cmsReadDevicelinkLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + + // What about abstract profiles? + if (ClassSig == cmsSigAbstractClass && i > 0) { + if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; + } + else { + _cmsMAT3identity(&m); + _cmsVEC3init(&off, 0, 0, 0); + } + + + if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; + + } + else { + + if (lIsInput) { + // Input direction means non-pcs connection, so proceed like devicelinks + Lut = _cmsReadInputLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + } + else { + + // Output direction means PCS connection. Intent may apply here + Lut = _cmsReadOutputLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + + + if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; + if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; + + } + } + + // Concatenate to the output LUT + if (!cmsPipelineCat(Result, Lut)) + goto Error; + + cmsPipelineFree(Lut); + Lut = NULL; + + // Update current space + CurrentColorSpace = ColorSpaceOut; + } + + // Check for non-negatives clip + if (dwFlags & cmsFLAGS_NONEGATIVES) { + + if (ColorSpaceOut == cmsSigGrayData || + ColorSpaceOut == cmsSigRgbData || + ColorSpaceOut == cmsSigCmykData) { + + cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOf(ColorSpaceOut)); + if (clip == NULL) goto Error; + + if (!cmsPipelineInsertStage(Result, cmsAT_END, clip)) + goto Error; + } + + } + + return Result; + +Error: + + if (Lut != NULL) cmsPipelineFree(Lut); + if (Result != NULL) cmsPipelineFree(Result); + return NULL; + + cmsUNUSED_PARAMETER(dwFlags); +} + + +// Wrapper for DLL calling convention +cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + return DefaultICCintents(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); +} + +// Black preserving intents --------------------------------------------------------------------------------------------- + +// Translate black-preserving intents to ICC ones +static +cmsUInt32Number TranslateNonICCIntents(cmsUInt32Number Intent) +{ + switch (Intent) { + case INTENT_PRESERVE_K_ONLY_PERCEPTUAL: + case INTENT_PRESERVE_K_PLANE_PERCEPTUAL: + return INTENT_PERCEPTUAL; + + case INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC: + case INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC: + return INTENT_RELATIVE_COLORIMETRIC; + + case INTENT_PRESERVE_K_ONLY_SATURATION: + case INTENT_PRESERVE_K_PLANE_SATURATION: + return INTENT_SATURATION; + + default: return Intent; + } +} + +// Sampler for Black-only preserving CMYK->CMYK transforms + +typedef struct { + cmsPipeline* cmyk2cmyk; // The original transform + cmsToneCurve* KTone; // Black-to-black tone curve + +} GrayOnlyParams; + + +// Preserve black only if that is the only ink used +static +int BlackPreservingGrayOnlySampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) +{ + GrayOnlyParams* bp = (GrayOnlyParams*) Cargo; + + // If going across black only, keep black only + if (In[0] == 0 && In[1] == 0 && In[2] == 0) { + + // TAC does not apply because it is black ink! + Out[0] = Out[1] = Out[2] = 0; + Out[3] = cmsEvalToneCurve16(bp->KTone, In[3]); + return TRUE; + } + + // Keep normal transform for other colors + bp ->cmyk2cmyk ->Eval16Fn(In, Out, bp ->cmyk2cmyk->Data); + return TRUE; +} + +// This is the entry for black-preserving K-only intents, which are non-ICC +static +cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + GrayOnlyParams bp; + cmsPipeline* Result; + cmsUInt32Number ICCIntents[256]; + cmsStage* CLUT; + cmsUInt32Number i, nGridPoints; + cmsUInt32Number lastProfilePos; + cmsUInt32Number preservationProfilesCount; + cmsHPROFILE hLastProfile; + + + // Sanity check + if (nProfiles < 1 || nProfiles > 255) return NULL; + + // Translate black-preserving intents to ICC ones + for (i=0; i < nProfiles; i++) + ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); + + + // Trim all CMYK devicelinks at the end + lastProfilePos = nProfiles - 1; + hLastProfile = hProfiles[lastProfilePos]; + + while (lastProfilePos > 1) + { + hLastProfile = hProfiles[--lastProfilePos]; + if (cmsGetColorSpace(hLastProfile) != cmsSigCmykData || + cmsGetDeviceClass(hLastProfile) != cmsSigLinkClass) + break; + } + + preservationProfilesCount = lastProfilePos + 1; + + // Check for non-cmyk profiles + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + !(cmsGetColorSpace(hLastProfile) == cmsSigCmykData || + cmsGetDeviceClass(hLastProfile) == cmsSigOutputClass)) + return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); + + // Allocate an empty LUT for holding the result + Result = cmsPipelineAlloc(ContextID, 4, 4); + if (Result == NULL) return NULL; + + memset(&bp, 0, sizeof(bp)); + + // Create a LUT holding normal ICC transform + bp.cmyk2cmyk = DefaultICCintents(ContextID, + preservationProfilesCount, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + + if (bp.cmyk2cmyk == NULL) goto Error; + + // Now, compute the tone curve + bp.KTone = _cmsBuildKToneCurve(ContextID, + 4096, + preservationProfilesCount, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + + if (bp.KTone == NULL) goto Error; + + + // How many gridpoints are we going to use? + nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); + + // Create the CLUT. 16 bits + CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); + if (CLUT == NULL) goto Error; + + // This is the one and only MPE in this LUT + if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) + goto Error; + + // Sample it. We cannot afford pre/post linearization this time. + if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0)) + goto Error; + + + // Insert possible devicelinks at the end + for (i = lastProfilePos + 1; i < nProfiles; i++) + { + cmsPipeline* devlink = _cmsReadDevicelinkLUT(hProfiles[i], ICCIntents[i]); + if (devlink == NULL) + goto Error; + + if (!cmsPipelineCat(Result, devlink)) + goto Error; + } + + + // Get rid of xform and tone curve + cmsPipelineFree(bp.cmyk2cmyk); + cmsFreeToneCurve(bp.KTone); + + return Result; + +Error: + + if (bp.cmyk2cmyk != NULL) cmsPipelineFree(bp.cmyk2cmyk); + if (bp.KTone != NULL) cmsFreeToneCurve(bp.KTone); + if (Result != NULL) cmsPipelineFree(Result); + return NULL; + +} + +// K Plane-preserving CMYK to CMYK ------------------------------------------------------------------------------------ + +typedef struct { + + cmsPipeline* cmyk2cmyk; // The original transform + cmsHTRANSFORM hProofOutput; // Output CMYK to Lab (last profile) + cmsHTRANSFORM cmyk2Lab; // The input chain + cmsToneCurve* KTone; // Black-to-black tone curve + cmsPipeline* LabK2cmyk; // The output profile + cmsFloat64Number MaxError; + + cmsHTRANSFORM hRoundTrip; + cmsFloat64Number MaxTAC; + + +} PreserveKPlaneParams; + + +// The CLUT will be stored at 16 bits, but calculations are performed at cmsFloat32Number precision +static +int BlackPreservingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) +{ + int i; + cmsFloat32Number Inf[4], Outf[4]; + cmsFloat32Number LabK[4]; + cmsFloat64Number SumCMY, SumCMYK, Error, Ratio; + cmsCIELab ColorimetricLab, BlackPreservingLab; + PreserveKPlaneParams* bp = (PreserveKPlaneParams*) Cargo; + + // Convert from 16 bits to floating point + for (i=0; i < 4; i++) + Inf[i] = (cmsFloat32Number) (In[i] / 65535.0); + + // Get the K across Tone curve + LabK[3] = cmsEvalToneCurveFloat(bp ->KTone, Inf[3]); + + // If going across black only, keep black only + if (In[0] == 0 && In[1] == 0 && In[2] == 0) { + + Out[0] = Out[1] = Out[2] = 0; + Out[3] = _cmsQuickSaturateWord(LabK[3] * 65535.0); + return TRUE; + } + + // Try the original transform, + cmsPipelineEvalFloat(Inf, Outf, bp ->cmyk2cmyk); + + // Store a copy of the floating point result into 16-bit + for (i=0; i < 4; i++) + Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0); + + // Maybe K is already ok (mostly on K=0) + if (fabsf(Outf[3] - LabK[3]) < (3.0 / 65535.0)) { + return TRUE; + } + + // K differ, measure and keep Lab measurement for further usage + // this is done in relative colorimetric intent + cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1); + + // Is not black only and the transform doesn't keep black. + // Obtain the Lab of output CMYK. After that we have Lab + K + cmsDoTransform(bp ->cmyk2Lab, Outf, LabK, 1); + + // Obtain the corresponding CMY using reverse interpolation + // (K is fixed in LabK[3]) + if (!cmsPipelineEvalReverseFloat(LabK, Outf, Outf, bp ->LabK2cmyk)) { + + // Cannot find a suitable value, so use colorimetric xform + // which is already stored in Out[] + return TRUE; + } + + // Make sure to pass through K (which now is fixed) + Outf[3] = LabK[3]; + + // Apply TAC if needed + SumCMY = (cmsFloat64Number) Outf[0] + Outf[1] + Outf[2]; + SumCMYK = SumCMY + Outf[3]; + + if (SumCMYK > bp ->MaxTAC) { + + Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY); + if (Ratio < 0) + Ratio = 0; + } + else + Ratio = 1.0; + + Out[0] = _cmsQuickSaturateWord(Outf[0] * Ratio * 65535.0); // C + Out[1] = _cmsQuickSaturateWord(Outf[1] * Ratio * 65535.0); // M + Out[2] = _cmsQuickSaturateWord(Outf[2] * Ratio * 65535.0); // Y + Out[3] = _cmsQuickSaturateWord(Outf[3] * 65535.0); + + // Estimate the error (this goes 16 bits to Lab DBL) + cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); + Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); + if (Error > bp -> MaxError) + bp->MaxError = Error; + + return TRUE; +} + + + +// This is the entry for black-plane preserving, which are non-ICC +static +cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + PreserveKPlaneParams bp; + + cmsPipeline* Result = NULL; + cmsUInt32Number ICCIntents[256]; + cmsStage* CLUT; + cmsUInt32Number i, nGridPoints; + cmsUInt32Number lastProfilePos; + cmsUInt32Number preservationProfilesCount; + cmsHPROFILE hLastProfile; + cmsHPROFILE hLab; + + // Sanity check + if (nProfiles < 1 || nProfiles > 255) return NULL; + + // Translate black-preserving intents to ICC ones + for (i=0; i < nProfiles; i++) + ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); + + // Trim all CMYK devicelinks at the end + lastProfilePos = nProfiles - 1; + hLastProfile = hProfiles[lastProfilePos]; + + while (lastProfilePos > 1) + { + hLastProfile = hProfiles[--lastProfilePos]; + if (cmsGetColorSpace(hLastProfile) != cmsSigCmykData || + cmsGetDeviceClass(hLastProfile) != cmsSigLinkClass) + break; + } + + preservationProfilesCount = lastProfilePos + 1; + + // Check for non-cmyk profiles + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + !(cmsGetColorSpace(hLastProfile) == cmsSigCmykData || + cmsGetDeviceClass(hLastProfile) == cmsSigOutputClass)) + return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); + + // Allocate an empty LUT for holding the result + Result = cmsPipelineAlloc(ContextID, 4, 4); + if (Result == NULL) return NULL; + + memset(&bp, 0, sizeof(bp)); + + // We need the input LUT of the last profile, assuming this one is responsible of + // black generation. This LUT will be searched in inverse order. + bp.LabK2cmyk = _cmsReadInputLUT(hLastProfile, INTENT_RELATIVE_COLORIMETRIC); + if (bp.LabK2cmyk == NULL) goto Cleanup; + + // Get total area coverage (in 0..1 domain) + bp.MaxTAC = cmsDetectTAC(hLastProfile) / 100.0; + if (bp.MaxTAC <= 0) goto Cleanup; + + + // Create a LUT holding normal ICC transform + bp.cmyk2cmyk = DefaultICCintents(ContextID, + preservationProfilesCount, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + if (bp.cmyk2cmyk == NULL) goto Cleanup; + + // Now the tone curve + bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, preservationProfilesCount, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + if (bp.KTone == NULL) goto Cleanup; + + // To measure the output, Last profile to Lab + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + bp.hProofOutput = cmsCreateTransformTHR(ContextID, hLastProfile, + CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + if ( bp.hProofOutput == NULL) goto Cleanup; + + // Same as anterior, but lab in the 0..1 range + bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hLastProfile, + FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, + FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4), + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + if (bp.cmyk2Lab == NULL) goto Cleanup; + cmsCloseProfile(hLab); + + // Error estimation (for debug only) + bp.MaxError = 0; + + // How many gridpoints are we going to use? + nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); + + + CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); + if (CLUT == NULL) goto Cleanup; + + if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) + goto Cleanup; + + cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); + + // Insert possible devicelinks at the end + for (i = lastProfilePos + 1; i < nProfiles; i++) + { + cmsPipeline* devlink = _cmsReadDevicelinkLUT(hProfiles[i], ICCIntents[i]); + if (devlink == NULL) + goto Cleanup; + + if (!cmsPipelineCat(Result, devlink)) + goto Cleanup; + } + + +Cleanup: + + if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk); + if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab); + if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput); + + if (bp.KTone) cmsFreeToneCurve(bp.KTone); + if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk); + + return Result; +} + + + +// Link routines ------------------------------------------------------------------------------------------------------ + +// Chain several profiles into a single LUT. It just checks the parameters and then calls the handler +// for the first intent in chain. The handler may be user-defined. Is up to the handler to deal with the +// rest of intents in chain. A maximum of 255 profiles at time are supported, which is pretty reasonable. +cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsIntentsList* Intent; + + // Make sure a reasonable number of profiles is provided + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't link '%d' profiles", nProfiles); + return NULL; + } + + for (i=0; i < nProfiles; i++) { + + // Check if black point is really needed or allowed. Note that + // following Adobe's document: + // BPC does not apply to devicelink profiles, nor to abs colorimetric, + // and applies always on V4 perceptual and saturation. + + if (TheIntents[i] == INTENT_ABSOLUTE_COLORIMETRIC) + BPC[i] = FALSE; + + if (TheIntents[i] == INTENT_PERCEPTUAL || TheIntents[i] == INTENT_SATURATION) { + + // Force BPC for V4 profiles in perceptual and saturation + if (cmsGetEncodedICCversion(hProfiles[i]) >= 0x4000000) + BPC[i] = TRUE; + } + } + + // Search for a handler. The first intent in the chain defines the handler. That would + // prevent using multiple custom intents in a multiintent chain, but the behaviour of + // this case would present some issues if the custom intent tries to do things like + // preserve primaries. This solution is not perfect, but works well on most cases. + + Intent = SearchIntent(ContextID, TheIntents[0]); + if (Intent == NULL) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]); + return NULL; + } + + // Call the handler + return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); +} + +// ------------------------------------------------------------------------------------------------- + +// Get information about available intents. nMax is the maximum space for the supplied "Codes" +// and "Descriptions" the function returns the total number of intents, which may be greater +// than nMax, although the matrices are not populated beyond this level. +cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); + cmsIntentsList* pt; + cmsUInt32Number nIntents; + + + for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next) + { + if (nIntents < nMax) { + if (Codes != NULL) + Codes[nIntents] = pt ->Intent; + + if (Descriptions != NULL) + Descriptions[nIntents] = pt ->Description; + } + + nIntents++; + } + + for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next) + { + if (nIntents < nMax) { + if (Codes != NULL) + Codes[nIntents] = pt ->Intent; + + if (Descriptions != NULL) + Descriptions[nIntents] = pt ->Description; + } + + nIntents++; + } + return nIntents; +} + +cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) +{ + return cmsGetSupportedIntentsTHR(NULL, nMax, Codes, Descriptions); +} + +// The plug-in registration. User can add new intents or override default routines +cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext id, cmsPluginBase* Data) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(id, IntentPlugin); + cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data; + cmsIntentsList* fl; + + // Do we have to reset the custom intents? + if (Data == NULL) { + + ctx->Intents = NULL; + return TRUE; + } + + fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList)); + if (fl == NULL) return FALSE; + + + fl ->Intent = Plugin ->Intent; + strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1); + fl ->Description[sizeof(fl ->Description)-1] = 0; + + fl ->Link = Plugin ->Link; + + fl ->Next = ctx ->Intents; + ctx ->Intents = fl; + + return TRUE; +} + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmserr.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmserr.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmserr.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmserr.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,692 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- + +#include "lcms2_internal.h" + + +// This function is here to help applications to prevent mixing lcms versions on header and shared objects. +int CMSEXPORT cmsGetEncodedCMMversion(void) +{ + return LCMS_VERSION; +} + +// I am so tired about incompatibilities on those functions that here are some replacements +// that hopefully would be fully portable. + +// compare two strings ignoring case +int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2) +{ + CMSREGISTER const unsigned char *us1 = (const unsigned char *)s1, + *us2 = (const unsigned char *)s2; + + while (toupper(*us1) == toupper(*us2++)) + if (*us1++ == '\0') + return 0; + + return (toupper(*us1) - toupper(*--us2)); +} + +// long int because C99 specifies ftell in such way (7.19.9.2) +long int CMSEXPORT cmsfilelength(FILE* f) +{ + long int p , n; + + p = ftell(f); // register current file position + if (p == -1L) + return -1L; + + if (fseek(f, 0, SEEK_END) != 0) { + return -1L; + } + + n = ftell(f); + fseek(f, p, SEEK_SET); // file position restored + + return n; +} + + +// Memory handling ------------------------------------------------------------------ +// +// This is the interface to low-level memory management routines. By default a simple +// wrapping to malloc/free/realloc is provided, although there is a limit on the max +// amount of memoy that can be reclaimed. This is mostly as a safety feature to prevent +// bogus or evil code to allocate huge blocks that otherwise lcms would never need. + +#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U)) + +// User may override this behaviour by using a memory plug-in, which basically replaces +// the default memory management functions. In this case, no check is performed and it +// is up to the plug-in writter to keep in the safe side. There are only three functions +// required to be implemented: malloc, realloc and free, although the user may want to +// replace the optional mallocZero, calloc and dup as well. + +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// ********************************************************************************* + +// This is the default memory allocation function. It does a very coarse +// check of amount of memory, just to prevent exploits +static +void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size) +{ + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never allow over maximum + + return (void*) malloc(size); + + cmsUNUSED_PARAMETER(ContextID); +} + +// Generic allocate & zero +static +void* _cmsMallocZeroDefaultFn(cmsContext ContextID, cmsUInt32Number size) +{ + void *pt = _cmsMalloc(ContextID, size); + if (pt == NULL) return NULL; + + memset(pt, 0, size); + return pt; +} + + +// The default free function. The only check proformed is against NULL pointers +static +void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr) +{ + // free(NULL) is defined a no-op by C99, therefore it is safe to + // avoid the check, but it is here just in case... + + if (Ptr) free(Ptr); + + cmsUNUSED_PARAMETER(ContextID); +} + +// The default realloc function. Again it checks for exploits. If Ptr is NULL, +// realloc behaves the same way as malloc and allocates a new block of size bytes. +static +void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size) +{ + + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never realloc over 512Mb + + return realloc(Ptr, size); + + cmsUNUSED_PARAMETER(ContextID); +} + + +// The default calloc function. Allocates an array of num elements, each one of size bytes +// all memory is initialized to zero. +static +void* _cmsCallocDefaultFn(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) +{ + cmsUInt32Number Total = num * size; + + // Preserve calloc behaviour + if (Total == 0) return NULL; + + // Safe check for overflow. + if (num >= UINT_MAX / size) return NULL; + + // Check for overflow + if (Total < num || Total < size) { + return NULL; + } + + if (Total > MAX_MEMORY_FOR_ALLOC) return NULL; // Never alloc over 512Mb + + return _cmsMallocZero(ContextID, Total); +} + +// Generic block duplication +static +void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number size) +{ + void* mem; + + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never dup over 512Mb + + mem = _cmsMalloc(ContextID, size); + + if (mem != NULL && Org != NULL) + memmove(mem, Org, size); + + return mem; +} + + +// Pointers to memory manager functions in Context0 +_cmsMemPluginChunkType _cmsMemPluginChunk = { _cmsMallocDefaultFn, _cmsMallocZeroDefaultFn, _cmsFreeDefaultFn, + _cmsReallocDefaultFn, _cmsCallocDefaultFn, _cmsDupDefaultFn + }; + + +// Reset and duplicate memory manager +void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Duplicate + ctx ->chunks[MemPlugin] = _cmsSubAllocDup(ctx ->MemPool, src ->chunks[MemPlugin], sizeof(_cmsMemPluginChunkType)); + } + else { + + // To reset it, we use the default allocators, which cannot be overridden + ctx ->chunks[MemPlugin] = &ctx ->DefaultMemoryManager; + } +} + +// Auxiliary to fill memory management functions from plugin (or context 0 defaults) +void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr) +{ + if (Plugin == NULL) { + + memcpy(ptr, &_cmsMemPluginChunk, sizeof(_cmsMemPluginChunk)); + } + else { + + ptr ->MallocPtr = Plugin -> MallocPtr; + ptr ->FreePtr = Plugin -> FreePtr; + ptr ->ReallocPtr = Plugin -> ReallocPtr; + + // Make sure we revert to defaults + ptr ->MallocZeroPtr= _cmsMallocZeroDefaultFn; + ptr ->CallocPtr = _cmsCallocDefaultFn; + ptr ->DupPtr = _cmsDupDefaultFn; + + if (Plugin ->MallocZeroPtr != NULL) ptr ->MallocZeroPtr = Plugin -> MallocZeroPtr; + if (Plugin ->CallocPtr != NULL) ptr ->CallocPtr = Plugin -> CallocPtr; + if (Plugin ->DupPtr != NULL) ptr ->DupPtr = Plugin -> DupPtr; + + } +} + + +// Plug-in replacement entry +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase *Data) +{ + cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data; + _cmsMemPluginChunkType* ptr; + + // NULL forces to reset to defaults. In this special case, the defaults are stored in the context structure. + // Remaining plug-ins does NOT have any copy in the context structure, but this is somehow special as the + // context internal data should be malloce'd by using those functions. + if (Data == NULL) { + + struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID; + + // Return to the default allocators + if (ContextID != NULL) { + ctx->chunks[MemPlugin] = (void*) &ctx->DefaultMemoryManager; + } + return TRUE; + } + + // Check for required callbacks + if (Plugin -> MallocPtr == NULL || + Plugin -> FreePtr == NULL || + Plugin -> ReallocPtr == NULL) return FALSE; + + // Set replacement functions + ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + if (ptr == NULL) + return FALSE; + + _cmsInstallAllocFunctions(Plugin, ptr); + return TRUE; +} + +// Generic allocate +void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr ->MallocPtr(ContextID, size); +} + +// Generic allocate & zero +void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->MallocZeroPtr(ContextID, size); +} + +// Generic calloc +void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->CallocPtr(ContextID, num, size); +} + +// Generic reallocate +void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->ReallocPtr(ContextID, Ptr, size); +} + +// Generic free memory +void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr) +{ + if (Ptr != NULL) { + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + ptr ->FreePtr(ContextID, Ptr); + } +} + +// Generic block duplication +void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr ->DupPtr(ContextID, Org, size); +} + +// ******************************************************************************************** + +// Sub allocation takes care of many pointers of small size. The memory allocated in +// this way have be freed at once. Next function allocates a single chunk for linked list +// I prefer this method over realloc due to the big inpact on xput realloc may have if +// memory is being swapped to disk. This approach is safer (although that may not be true on all platforms) +static +_cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial) +{ + _cmsSubAllocator_chunk* chunk; + + // 20K by default + if (Initial == 0) + Initial = 20*1024; + + // Create the container + chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk)); + if (chunk == NULL) return NULL; + + // Initialize values + chunk ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, Initial); + if (chunk ->Block == NULL) { + + // Something went wrong + _cmsFree(ContextID, chunk); + return NULL; + } + + chunk ->BlockSize = Initial; + chunk ->Used = 0; + chunk ->next = NULL; + + return chunk; +} + +// The suballocated is nothing but a pointer to the first element in the list. We also keep +// the thread ID in this structure. +_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial) +{ + _cmsSubAllocator* sub; + + // Create the container + sub = (_cmsSubAllocator*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator)); + if (sub == NULL) return NULL; + + sub ->ContextID = ContextID; + + sub ->h = _cmsCreateSubAllocChunk(ContextID, Initial); + if (sub ->h == NULL) { + _cmsFree(ContextID, sub); + return NULL; + } + + return sub; +} + + +// Get rid of whole linked list +void _cmsSubAllocDestroy(_cmsSubAllocator* sub) +{ + _cmsSubAllocator_chunk *chunk, *n; + + for (chunk = sub ->h; chunk != NULL; chunk = n) { + + n = chunk->next; + if (chunk->Block != NULL) _cmsFree(sub ->ContextID, chunk->Block); + _cmsFree(sub ->ContextID, chunk); + } + + // Free the header + _cmsFree(sub ->ContextID, sub); +} + + +// Get a pointer to small memory block. +void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size) +{ + cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used; + cmsUInt8Number* ptr; + + size = _cmsALIGNMEM(size); + + // Check for memory. If there is no room, allocate a new chunk of double memory size. + if (size > Free) { + + _cmsSubAllocator_chunk* chunk; + cmsUInt32Number newSize; + + newSize = sub -> h ->BlockSize * 2; + if (newSize < size) newSize = size; + + chunk = _cmsCreateSubAllocChunk(sub -> ContextID, newSize); + if (chunk == NULL) return NULL; + + // Link list + chunk ->next = sub ->h; + sub ->h = chunk; + + } + + ptr = sub -> h ->Block + sub -> h ->Used; + sub -> h -> Used += size; + + return (void*) ptr; +} + +// Duplicate in pool +void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size) +{ + void *NewPtr; + + // Dup of null pointer is also NULL + if (ptr == NULL) + return NULL; + + NewPtr = _cmsSubAlloc(s, size); + + if (ptr != NULL && NewPtr != NULL) { + memcpy(NewPtr, ptr, size); + } + + return NewPtr; +} + + + +// Error logging ****************************************************************** + +// There is no error handling at all. When a function fails, it returns proper value. +// For example, all create functions does return NULL on failure. Other return FALSE +// It may be interesting, for the developer, to know why the function is failing. +// for that reason, lcms2 does offer a logging function. This function does receive +// a ENGLISH string with some clues on what is going wrong. You can show this +// info to the end user, or just create some sort of log. +// The logging function should NOT terminate the program, as this obviously can leave +// resources. It is the programmer's responsibility to check each function return code +// to make sure it didn't fail. + +// Error messages are limited to MAX_ERROR_MESSAGE_LEN + +#define MAX_ERROR_MESSAGE_LEN 1024 + +// --------------------------------------------------------------------------------------------------------- + +// This is our default log error +static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); + +// Context0 storage, which is global +_cmsLogErrorChunkType _cmsLogErrorChunk = { DefaultLogErrorHandlerFunction }; + +// Allocates and inits error logger container for a given context. If src is NULL, only initializes the value +// to the default. Otherwise, it duplicates the value. The interface is standard across all context clients +void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsLogErrorChunkType LogErrorChunk = { DefaultLogErrorHandlerFunction }; + void* from; + + if (src != NULL) { + from = src ->chunks[Logger]; + } + else { + from = &LogErrorChunk; + } + + ctx ->chunks[Logger] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsLogErrorChunkType)); +} + +// The default error logger does nothing. +static +void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text) +{ + // fprintf(stderr, "[lcms]: %s\n", Text); + // fflush(stderr); + + cmsUNUSED_PARAMETER(ContextID); + cmsUNUSED_PARAMETER(ErrorCode); + cmsUNUSED_PARAMETER(Text); +} + +// Change log error, context based +void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn) +{ + _cmsLogErrorChunkType* lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger); + + if (lhg != NULL) { + + if (Fn == NULL) + lhg -> LogErrorHandler = DefaultLogErrorHandlerFunction; + else + lhg -> LogErrorHandler = Fn; + } +} + +// Change log error, legacy +void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn) +{ + cmsSetLogErrorHandlerTHR(NULL, Fn); +} + +// Log an error +// ErrorText is a text holding an english description of error. +void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...) +{ + va_list args; + char Buffer[MAX_ERROR_MESSAGE_LEN]; + _cmsLogErrorChunkType* lhg; + + + va_start(args, ErrorText); + vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args); + va_end(args); + + // Check for the context, if specified go there. If not, go for the global + lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger); + if (lhg ->LogErrorHandler) { + lhg ->LogErrorHandler(ContextID, ErrorCode, Buffer); + } +} + +// Utility function to print signatures +void _cmsTagSignature2String(char String[5], cmsTagSignature sig) +{ + cmsUInt32Number be; + + // Convert to big endian + be = _cmsAdjustEndianess32((cmsUInt32Number) sig); + + // Move chars + memmove(String, &be, 4); + + // Make sure of terminator + String[4] = 0; +} + +//-------------------------------------------------------------------------------------------------- + + +static +void* defMtxCreate(cmsContext id) +{ + _cmsMutex* ptr_mutex = (_cmsMutex*) _cmsMalloc(id, sizeof(_cmsMutex)); + _cmsInitMutexPrimitive(ptr_mutex); + return (void*) ptr_mutex; +} + +static +void defMtxDestroy(cmsContext id, void* mtx) +{ + _cmsDestroyMutexPrimitive((_cmsMutex *) mtx); + _cmsFree(id, mtx); +} + +static +cmsBool defMtxLock(cmsContext id, void* mtx) +{ + cmsUNUSED_PARAMETER(id); + return _cmsLockPrimitive((_cmsMutex *) mtx) == 0; +} + +static +void defMtxUnlock(cmsContext id, void* mtx) +{ + cmsUNUSED_PARAMETER(id); + _cmsUnlockPrimitive((_cmsMutex *) mtx); +} + + + +// Pointers to memory manager functions in Context0 +_cmsMutexPluginChunkType _cmsMutexPluginChunk = { defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; + +// Allocate and init mutex container. +void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsMutexPluginChunkType MutexChunk = {defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; + void* from; + + if (src != NULL) { + from = src ->chunks[MutexPlugin]; + } + else { + from = &MutexChunk; + } + + ctx ->chunks[MutexPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsMutexPluginChunkType)); +} + +// Register new ways to transform +cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginMutex* Plugin = (cmsPluginMutex*) Data; + _cmsMutexPluginChunkType* ctx = ( _cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (Data == NULL) { + + // No lock routines + ctx->CreateMutexPtr = NULL; + ctx->DestroyMutexPtr = NULL; + ctx->LockMutexPtr = NULL; + ctx ->UnlockMutexPtr = NULL; + return TRUE; + } + + // Factory callback is required + if (Plugin ->CreateMutexPtr == NULL || Plugin ->DestroyMutexPtr == NULL || + Plugin ->LockMutexPtr == NULL || Plugin ->UnlockMutexPtr == NULL) return FALSE; + + + ctx->CreateMutexPtr = Plugin->CreateMutexPtr; + ctx->DestroyMutexPtr = Plugin ->DestroyMutexPtr; + ctx ->LockMutexPtr = Plugin ->LockMutexPtr; + ctx ->UnlockMutexPtr = Plugin ->UnlockMutexPtr; + + // All is ok + return TRUE; +} + +// Generic Mutex fns +void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->CreateMutexPtr == NULL) return NULL; + + return ptr ->CreateMutexPtr(ContextID); +} + +void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->DestroyMutexPtr != NULL) { + + ptr ->DestroyMutexPtr(ContextID, mtx); + } +} + +cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->LockMutexPtr == NULL) return TRUE; + + return ptr ->LockMutexPtr(ContextID, mtx); +} + +void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->UnlockMutexPtr != NULL) { + + ptr ->UnlockMutexPtr(ContextID, mtx); + } +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsgamma.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsgamma.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsgamma.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsgamma.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1517 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +#include "lcms2_internal.h" + +// Tone curves are powerful constructs that can contain curves specified in diverse ways. +// The curve is stored in segments, where each segment can be sampled or specified by parameters. +// a 16.bit simplification of the *whole* curve is kept for optimization purposes. For float operation, +// each segment is evaluated separately. Plug-ins may be used to define new parametric schemes, +// each plug-in may define up to MAX_TYPES_IN_LCMS_PLUGIN functions types. For defining a function, +// the plug-in should provide the type id, how many parameters each type has, and a pointer to +// a procedure that evaluates the function. In the case of reverse evaluation, the evaluator will +// be called with the type id as a negative value, and a sampled version of the reversed curve +// will be built. + +// ----------------------------------------------------------------- Implementation +// Maxim number of nodes +#define MAX_NODES_IN_CURVE 4097 +#define MINUS_INF (-1E22F) +#define PLUS_INF (+1E22F) + +// The list of supported parametric curves +typedef struct _cmsParametricCurvesCollection_st { + + cmsUInt32Number nFunctions; // Number of supported functions in this chunk + cmsInt32Number FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types + cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function + + cmsParametricCurveEvaluator Evaluator; // The evaluator + + struct _cmsParametricCurvesCollection_st* Next; // Next in list + +} _cmsParametricCurvesCollection; + +// This is the default (built-in) evaluator +static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R); + +// The built-in list +static _cmsParametricCurvesCollection DefaultCurves = { + 10, // # of curve types + { 1, 2, 3, 4, 5, 6, 7, 8, 108, 109 }, // Parametric curve ID + { 1, 3, 4, 5, 7, 4, 5, 5, 1, 1 }, // Parameters by type + DefaultEvalParametricFn, // Evaluator + NULL // Next in chain +}; + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginCurvesList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsCurvesPluginChunkType newHead = { NULL }; + _cmsParametricCurvesCollection* entry; + _cmsParametricCurvesCollection* Anterior = NULL; + _cmsCurvesPluginChunkType* head = (_cmsCurvesPluginChunkType*) src->chunks[CurvesPlugin]; + + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->ParametricCurves; + entry != NULL; + entry = entry ->Next) { + + _cmsParametricCurvesCollection *newEntry = ( _cmsParametricCurvesCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsParametricCurvesCollection)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.ParametricCurves == NULL) + newHead.ParametricCurves = newEntry; + } + + ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsCurvesPluginChunkType)); +} + +// The allocator have to follow the chain +void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Copy all linked list + DupPluginCurvesList(ctx, src); + } + else { + static _cmsCurvesPluginChunkType CurvesPluginChunk = { NULL }; + ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx ->MemPool, &CurvesPluginChunk, sizeof(_cmsCurvesPluginChunkType)); + } +} + + +// The linked list head +_cmsCurvesPluginChunkType _cmsCurvesPluginChunk = { NULL }; + +// As a way to install new parametric curves +cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); + cmsPluginParametricCurves* Plugin = (cmsPluginParametricCurves*) Data; + _cmsParametricCurvesCollection* fl; + + if (Data == NULL) { + + ctx -> ParametricCurves = NULL; + return TRUE; + } + + fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsParametricCurvesCollection)); + if (fl == NULL) return FALSE; + + // Copy the parameters + fl ->Evaluator = Plugin ->Evaluator; + fl ->nFunctions = Plugin ->nFunctions; + + // Make sure no mem overwrites + if (fl ->nFunctions > MAX_TYPES_IN_LCMS_PLUGIN) + fl ->nFunctions = MAX_TYPES_IN_LCMS_PLUGIN; + + // Copy the data + memmove(fl->FunctionTypes, Plugin ->FunctionTypes, fl->nFunctions * sizeof(cmsUInt32Number)); + memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number)); + + // Keep linked list + fl ->Next = ctx->ParametricCurves; + ctx->ParametricCurves = fl; + + // All is ok + return TRUE; +} + + +// Search in type list, return position or -1 if not found +static +int IsInSet(int Type, _cmsParametricCurvesCollection* c) +{ + int i; + + for (i=0; i < (int) c ->nFunctions; i++) + if (abs(Type) == c ->FunctionTypes[i]) return i; + + return -1; +} + + +// Search for the collection which contains a specific type +static +_cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index) +{ + _cmsParametricCurvesCollection* c; + int Position; + _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); + + for (c = ctx->ParametricCurves; c != NULL; c = c ->Next) { + + Position = IsInSet(Type, c); + + if (Position != -1) { + if (index != NULL) + *index = Position; + return c; + } + } + // If none found, revert for defaults + for (c = &DefaultCurves; c != NULL; c = c ->Next) { + + Position = IsInSet(Type, c); + + if (Position != -1) { + if (index != NULL) + *index = Position; + return c; + } + } + + return NULL; +} + +// Low level allocate, which takes care of memory details. nEntries may be zero, and in this case +// no optimation curve is computed. nSegments may also be zero in the inverse case, where only the +// optimization curve is given. Both features simultaneously is an error +static +cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEntries, + cmsUInt32Number nSegments, const cmsCurveSegment* Segments, + const cmsUInt16Number* Values) +{ + cmsToneCurve* p; + cmsUInt32Number i; + + // We allow huge tables, which are then restricted for smoothing operations + if (nEntries > 65530) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve of more than 65530 entries"); + return NULL; + } + + if (nEntries == 0 && nSegments == 0) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve with zero segments and no table"); + return NULL; + } + + // Allocate all required pointers, etc. + p = (cmsToneCurve*) _cmsMallocZero(ContextID, sizeof(cmsToneCurve)); + if (!p) return NULL; + + // In this case, there are no segments + if (nSegments == 0) { + p ->Segments = NULL; + p ->Evals = NULL; + } + else { + p ->Segments = (cmsCurveSegment*) _cmsCalloc(ContextID, nSegments, sizeof(cmsCurveSegment)); + if (p ->Segments == NULL) goto Error; + + p ->Evals = (cmsParametricCurveEvaluator*) _cmsCalloc(ContextID, nSegments, sizeof(cmsParametricCurveEvaluator)); + if (p ->Evals == NULL) goto Error; + } + + p -> nSegments = nSegments; + + // This 16-bit table contains a limited precision representation of the whole curve and is kept for + // increasing xput on certain operations. + if (nEntries == 0) { + p ->Table16 = NULL; + } + else { + p ->Table16 = (cmsUInt16Number*) _cmsCalloc(ContextID, nEntries, sizeof(cmsUInt16Number)); + if (p ->Table16 == NULL) goto Error; + } + + p -> nEntries = nEntries; + + // Initialize members if requested + if (Values != NULL && (nEntries > 0)) { + + for (i=0; i < nEntries; i++) + p ->Table16[i] = Values[i]; + } + + // Initialize the segments stuff. The evaluator for each segment is located and a pointer to it + // is placed in advance to maximize performance. + if (Segments != NULL && (nSegments > 0)) { + + _cmsParametricCurvesCollection *c; + + p ->SegInterp = (cmsInterpParams**) _cmsCalloc(ContextID, nSegments, sizeof(cmsInterpParams*)); + if (p ->SegInterp == NULL) goto Error; + + for (i=0; i < nSegments; i++) { + + // Type 0 is a special marker for table-based curves + if (Segments[i].Type == 0) + p ->SegInterp[i] = _cmsComputeInterpParams(ContextID, Segments[i].nGridPoints, 1, 1, NULL, CMS_LERP_FLAGS_FLOAT); + + memmove(&p ->Segments[i], &Segments[i], sizeof(cmsCurveSegment)); + + if (Segments[i].Type == 0 && Segments[i].SampledPoints != NULL) + p ->Segments[i].SampledPoints = (cmsFloat32Number*) _cmsDupMem(ContextID, Segments[i].SampledPoints, sizeof(cmsFloat32Number) * Segments[i].nGridPoints); + else + p ->Segments[i].SampledPoints = NULL; + + + c = GetParametricCurveByType(ContextID, Segments[i].Type, NULL); + if (c != NULL) + p ->Evals[i] = c ->Evaluator; + } + } + + p ->InterpParams = _cmsComputeInterpParams(ContextID, p ->nEntries, 1, 1, p->Table16, CMS_LERP_FLAGS_16BITS); + if (p->InterpParams != NULL) + return p; + +Error: + if (p -> SegInterp) _cmsFree(ContextID, p -> SegInterp); + if (p -> Segments) _cmsFree(ContextID, p -> Segments); + if (p -> Evals) _cmsFree(ContextID, p -> Evals); + if (p ->Table16) _cmsFree(ContextID, p ->Table16); + _cmsFree(ContextID, p); + return NULL; +} + + +// Generates a sigmoidal function with desired steepness. +cmsINLINE double sigmoid_base(double k, double t) +{ + return (1.0 / (1.0 + exp(-k * t))) - 0.5; +} + +cmsINLINE double inverted_sigmoid_base(double k, double t) +{ + return -log((1.0 / (t + 0.5)) - 1.0) / k; +} + +cmsINLINE double sigmoid_factory(double k, double t) +{ + double correction = 0.5 / sigmoid_base(k, 1); + + return correction * sigmoid_base(k, 2.0 * t - 1.0) + 0.5; +} + +cmsINLINE double inverse_sigmoid_factory(double k, double t) +{ + double correction = 0.5 / sigmoid_base(k, 1); + + return (inverted_sigmoid_base(k, (t - 0.5) / correction) + 1.0) / 2.0; +} + + +// Parametric Fn using floating point +static +cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R) +{ + cmsFloat64Number e, Val, disc; + + switch (Type) { + + // X = Y ^ Gamma + case 1: + if (R < 0) { + + if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) + Val = R; + else + Val = 0; + } + else + Val = pow(R, Params[0]); + break; + + // Type 1 Reversed: X = Y ^1/gamma + case -1: + if (R < 0) { + + if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) + Val = R; + else + Val = 0; + } + else + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE) + Val = PLUS_INF; + else + Val = pow(R, 1 / Params[0]); + } + break; + + // CIE 122-1966 + // Y = (aX + b)^Gamma | X >= -b/a + // Y = 0 | else + case 2: + { + + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + disc = -Params[2] / Params[1]; + + if (R >= disc) { + + e = Params[1] * R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]); + else + Val = 0; + } + else + Val = 0; + } + } + break; + + // Type 2 Reversed + // X = (Y ^1/g - b) / a + case -2: + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + if (R < 0) + Val = 0; + else + Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1]; + + if (Val < 0) + Val = 0; + } + } + break; + + + // IEC 61966-3 + // Y = (aX + b)^Gamma | X <= -b/a + // Y = c | else + case 3: + { + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + disc = -Params[2] / Params[1]; + if (disc < 0) + disc = 0; + + if (R >= disc) { + + e = Params[1] * R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]) + Params[3]; + else + Val = 0; + } + else + Val = Params[3]; + } + } + break; + + + // Type 3 reversed + // X=((Y-c)^1/g - b)/a | (Y>=c) + // X=-b/a | (Y= Params[3]) { + + e = R - Params[3]; + + if (e > 0) + Val = (pow(e, 1 / Params[0]) - Params[2]) / Params[1]; + else + Val = 0; + } + else { + Val = -Params[2] / Params[1]; + } + } + } + break; + + + // IEC 61966-2.1 (sRGB) + // Y = (aX + b)^Gamma | X >= d + // Y = cX | X < d + case 4: + if (R >= Params[4]) { + + e = Params[1]*R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]); + else + Val = 0; + } + else + Val = R * Params[3]; + break; + + // Type 4 reversed + // X=((Y^1/g-b)/a) | Y >= (ad+b)^g + // X=Y/c | Y< (ad+b)^g + case -4: + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE || + fabs(Params[3]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + e = Params[1] * Params[4] + Params[2]; + if (e < 0) + disc = 0; + else + disc = pow(e, Params[0]); + + if (R >= disc) { + + Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1]; + } + else { + Val = R / Params[3]; + } + } + } + break; + + + // Y = (aX + b)^Gamma + e | X >= d + // Y = cX + f | X < d + case 5: + if (R >= Params[4]) { + + e = Params[1]*R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]) + Params[5]; + else + Val = Params[5]; + } + else + Val = R*Params[3] + Params[6]; + break; + + + // Reversed type 5 + // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e), cd+f + // X=(Y-f)/c | else + case -5: + { + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE || + fabs(Params[3]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + disc = Params[3] * Params[4] + Params[6]; + if (R >= disc) { + + e = R - Params[5]; + if (e < 0) + Val = 0; + else + Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1]; + } + else { + Val = (R - Params[6]) / Params[3]; + } + } + } + break; + + + // Types 6,7,8 comes from segmented curves as described in ICCSpecRevision_02_11_06_Float.pdf + // Type 6 is basically identical to type 5 without d + + // Y = (a * X + b) ^ Gamma + c + case 6: + e = Params[1]*R + Params[2]; + + if (e < 0) + Val = Params[3]; + else + Val = pow(e, Params[0]) + Params[3]; + break; + + // ((Y - c) ^1/Gamma - b) / a + case -6: + { + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + e = R - Params[3]; + if (e < 0) + Val = 0; + else + Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1]; + } + } + break; + + + // Y = a * log (b * X^Gamma + c) + d + case 7: + + e = Params[2] * pow(R, Params[0]) + Params[3]; + if (e <= 0) + Val = Params[4]; + else + Val = Params[1]*log10(e) + Params[4]; + break; + + // (Y - d) / a = log(b * X ^Gamma + c) + // pow(10, (Y-d) / a) = b * X ^Gamma + c + // pow((pow(10, (Y-d) / a) - c) / b, 1/g) = X + case -7: + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE || + fabs(Params[2]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + Val = pow((pow(10.0, (R - Params[4]) / Params[1]) - Params[3]) / Params[2], 1.0 / Params[0]); + } + } + break; + + + //Y = a * b^(c*X+d) + e + case 8: + Val = (Params[0] * pow(Params[1], Params[2] * R + Params[3]) + Params[4]); + break; + + + // Y = (log((y-e) / a) / log(b) - d ) / c + // a=0, b=1, c=2, d=3, e=4, + case -8: + + disc = R - Params[4]; + if (disc < 0) Val = 0; + else + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[2]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + Val = (log(disc / Params[0]) / log(Params[1]) - Params[3]) / Params[2]; + } + } + break; + + + // S-Shaped: (1 - (1-x)^1/g)^1/g + case 108: + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE) + Val = 0; + else + Val = pow(1.0 - pow(1 - R, 1/Params[0]), 1/Params[0]); + break; + + // y = (1 - (1-x)^1/g)^1/g + // y^g = (1 - (1-x)^1/g) + // 1 - y^g = (1-x)^1/g + // (1 - y^g)^g = 1 - x + // 1 - (1 - y^g)^g + case -108: + Val = 1 - pow(1 - pow(R, Params[0]), Params[0]); + break; + + // Sigmoidals + case 109: + Val = sigmoid_factory(Params[0], R); + break; + + case -109: + Val = inverse_sigmoid_factory(Params[0], R); + break; + + default: + // Unsupported parametric curve. Should never reach here + return 0; + } + + return Val; +} + +// Evaluate a segmented function for a single value. Return -Inf if no valid segment found . +// If fn type is 0, perform an interpolation on the table +static +cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R) +{ + int i; + cmsFloat32Number Out32; + cmsFloat64Number Out; + + for (i = (int) g->nSegments - 1; i >= 0; --i) { + + // Check for domain + if ((R > g->Segments[i].x0) && (R <= g->Segments[i].x1)) { + + // Type == 0 means segment is sampled + if (g->Segments[i].Type == 0) { + + cmsFloat32Number R1 = (cmsFloat32Number)(R - g->Segments[i].x0) / (g->Segments[i].x1 - g->Segments[i].x0); + + // Setup the table (TODO: clean that) + g->SegInterp[i]->Table = g->Segments[i].SampledPoints; + + g->SegInterp[i]->Interpolation.LerpFloat(&R1, &Out32, g->SegInterp[i]); + Out = (cmsFloat64Number) Out32; + + } + else { + Out = g->Evals[i](g->Segments[i].Type, g->Segments[i].Params, R); + } + + if (isinf(Out)) + return PLUS_INF; + else + { + if (isinf(-Out)) + return MINUS_INF; + } + + return Out; + } + } + + return MINUS_INF; +} + +// Access to estimated low-res table +cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + return t ->nEntries; +} + +const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + return t ->Table16; +} + + +// Create an empty gamma curve, by using tables. This specifies only the limited-precision part, and leaves the +// floating point description empty. +cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsUInt32Number nEntries, const cmsUInt16Number Values[]) +{ + return AllocateToneCurveStruct(ContextID, nEntries, 0, NULL, Values); +} + +static +cmsUInt32Number EntriesByGamma(cmsFloat64Number Gamma) +{ + if (fabs(Gamma - 1.0) < 0.001) return 2; + return 4096; +} + + +// Create a segmented gamma, fill the table +cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, + cmsUInt32Number nSegments, const cmsCurveSegment Segments[]) +{ + cmsUInt32Number i; + cmsFloat64Number R, Val; + cmsToneCurve* g; + cmsUInt32Number nGridPoints = 4096; + + _cmsAssert(Segments != NULL); + + // Optimizatin for identity curves. + if (nSegments == 1 && Segments[0].Type == 1) { + + nGridPoints = EntriesByGamma(Segments[0].Params[0]); + } + + g = AllocateToneCurveStruct(ContextID, nGridPoints, nSegments, Segments, NULL); + if (g == NULL) return NULL; + + // Once we have the floating point version, we can approximate a 16 bit table of 4096 entries + // for performance reasons. This table would normally not be used except on 8/16 bits transforms. + for (i = 0; i < nGridPoints; i++) { + + R = (cmsFloat64Number) i / (nGridPoints-1); + + Val = EvalSegmentedFn(g, R); + + // Round and saturate + g ->Table16[i] = _cmsQuickSaturateWord(Val * 65535.0); + } + + return g; +} + +// Use a segmented curve to store the floating point table +cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]) +{ + cmsCurveSegment Seg[3]; + + // A segmented tone curve should have function segments in the first and last positions + // Initialize segmented curve part up to 0 to constant value = samples[0] + Seg[0].x0 = MINUS_INF; + Seg[0].x1 = 0; + Seg[0].Type = 6; + + Seg[0].Params[0] = 1; + Seg[0].Params[1] = 0; + Seg[0].Params[2] = 0; + Seg[0].Params[3] = values[0]; + Seg[0].Params[4] = 0; + + // From zero to 1 + Seg[1].x0 = 0; + Seg[1].x1 = 1.0; + Seg[1].Type = 0; + + Seg[1].nGridPoints = nEntries; + Seg[1].SampledPoints = (cmsFloat32Number*) values; + + // Final segment is constant = lastsample + Seg[2].x0 = 1.0; + Seg[2].x1 = PLUS_INF; + Seg[2].Type = 6; + + Seg[2].Params[0] = 1; + Seg[2].Params[1] = 0; + Seg[2].Params[2] = 0; + Seg[2].Params[3] = values[nEntries-1]; + Seg[2].Params[4] = 0; + + + return cmsBuildSegmentedToneCurve(ContextID, 3, Seg); +} + +// Parametric curves +// +// Parameters goes as: Curve, a, b, c, d, e, f +// Type is the ICC type +1 +// if type is negative, then the curve is analytically inverted +cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]) +{ + cmsCurveSegment Seg0; + int Pos = 0; + cmsUInt32Number size; + _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos); + + _cmsAssert(Params != NULL); + + if (c == NULL) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type); + return NULL; + } + + memset(&Seg0, 0, sizeof(Seg0)); + + Seg0.x0 = MINUS_INF; + Seg0.x1 = PLUS_INF; + Seg0.Type = Type; + + size = c->ParameterCount[Pos] * sizeof(cmsFloat64Number); + memmove(Seg0.Params, Params, size); + + return cmsBuildSegmentedToneCurve(ContextID, 1, &Seg0); +} + + + +// Build a gamma table based on gamma constant +cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma) +{ + return cmsBuildParametricToneCurve(ContextID, 1, &Gamma); +} + + +// Free all memory taken by the gamma curve +void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve) +{ + cmsContext ContextID; + + if (Curve == NULL) return; + + ContextID = Curve ->InterpParams->ContextID; + + _cmsFreeInterpParams(Curve ->InterpParams); + + if (Curve -> Table16) + _cmsFree(ContextID, Curve ->Table16); + + if (Curve ->Segments) { + + cmsUInt32Number i; + + for (i=0; i < Curve ->nSegments; i++) { + + if (Curve ->Segments[i].SampledPoints) { + _cmsFree(ContextID, Curve ->Segments[i].SampledPoints); + } + + if (Curve ->SegInterp[i] != 0) + _cmsFreeInterpParams(Curve->SegInterp[i]); + } + + _cmsFree(ContextID, Curve ->Segments); + _cmsFree(ContextID, Curve ->SegInterp); + } + + if (Curve -> Evals) + _cmsFree(ContextID, Curve -> Evals); + + _cmsFree(ContextID, Curve); +} + +// Utility function, free 3 gamma tables +void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]) +{ + + _cmsAssert(Curve != NULL); + + if (Curve[0] != NULL) cmsFreeToneCurve(Curve[0]); + if (Curve[1] != NULL) cmsFreeToneCurve(Curve[1]); + if (Curve[2] != NULL) cmsFreeToneCurve(Curve[2]); + + Curve[0] = Curve[1] = Curve[2] = NULL; +} + + +// Duplicate a gamma table +cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* In) +{ + if (In == NULL) return NULL; + + return AllocateToneCurveStruct(In ->InterpParams ->ContextID, In ->nEntries, In ->nSegments, In ->Segments, In ->Table16); +} + +// Joins two curves for X and Y. Curves should be monotonic. +// We want to get +// +// y = Y^-1(X(t)) +// +cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, + const cmsToneCurve* X, + const cmsToneCurve* Y, cmsUInt32Number nResultingPoints) +{ + cmsToneCurve* out = NULL; + cmsToneCurve* Yreversed = NULL; + cmsFloat32Number t, x; + cmsFloat32Number* Res = NULL; + cmsUInt32Number i; + + + _cmsAssert(X != NULL); + _cmsAssert(Y != NULL); + + Yreversed = cmsReverseToneCurveEx(nResultingPoints, Y); + if (Yreversed == NULL) goto Error; + + Res = (cmsFloat32Number*) _cmsCalloc(ContextID, nResultingPoints, sizeof(cmsFloat32Number)); + if (Res == NULL) goto Error; + + //Iterate + for (i=0; i < nResultingPoints; i++) { + + t = (cmsFloat32Number) i / (cmsFloat32Number)(nResultingPoints-1); + x = cmsEvalToneCurveFloat(X, t); + Res[i] = cmsEvalToneCurveFloat(Yreversed, x); + } + + // Allocate space for output + out = cmsBuildTabulatedToneCurveFloat(ContextID, nResultingPoints, Res); + +Error: + + if (Res != NULL) _cmsFree(ContextID, Res); + if (Yreversed != NULL) cmsFreeToneCurve(Yreversed); + + return out; +} + + + +// Get the surrounding nodes. This is tricky on non-monotonic tables +static +int GetInterval(cmsFloat64Number In, const cmsUInt16Number LutTable[], const struct _cms_interp_struc* p) +{ + int i; + int y0, y1; + + // A 1 point table is not allowed + if (p -> Domain[0] < 1) return -1; + + // Let's see if ascending or descending. + if (LutTable[0] < LutTable[p ->Domain[0]]) { + + // Table is overall ascending + for (i = (int) p->Domain[0] - 1; i >= 0; --i) { + + y0 = LutTable[i]; + y1 = LutTable[i+1]; + + if (y0 <= y1) { // Increasing + if (In >= y0 && In <= y1) return i; + } + else + if (y1 < y0) { // Decreasing + if (In >= y1 && In <= y0) return i; + } + } + } + else { + // Table is overall descending + for (i=0; i < (int) p -> Domain[0]; i++) { + + y0 = LutTable[i]; + y1 = LutTable[i+1]; + + if (y0 <= y1) { // Increasing + if (In >= y0 && In <= y1) return i; + } + else + if (y1 < y0) { // Decreasing + if (In >= y1 && In <= y0) return i; + } + } + } + + return -1; +} + +// Reverse a gamma table +cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsUInt32Number nResultSamples, const cmsToneCurve* InCurve) +{ + cmsToneCurve *out; + cmsFloat64Number a = 0, b = 0, y, x1, y1, x2, y2; + int i, j; + int Ascending; + + _cmsAssert(InCurve != NULL); + + // Try to reverse it analytically whatever possible + + if (InCurve ->nSegments == 1 && InCurve ->Segments[0].Type > 0 && + /* InCurve -> Segments[0].Type <= 5 */ + GetParametricCurveByType(InCurve ->InterpParams->ContextID, InCurve ->Segments[0].Type, NULL) != NULL) { + + return cmsBuildParametricToneCurve(InCurve ->InterpParams->ContextID, + -(InCurve -> Segments[0].Type), + InCurve -> Segments[0].Params); + } + + // Nope, reverse the table. + out = cmsBuildTabulatedToneCurve16(InCurve ->InterpParams->ContextID, nResultSamples, NULL); + if (out == NULL) + return NULL; + + // We want to know if this is an ascending or descending table + Ascending = !cmsIsToneCurveDescending(InCurve); + + // Iterate across Y axis + for (i=0; i < (int) nResultSamples; i++) { + + y = (cmsFloat64Number) i * 65535.0 / (nResultSamples - 1); + + // Find interval in which y is within. + j = GetInterval(y, InCurve->Table16, InCurve->InterpParams); + if (j >= 0) { + + + // Get limits of interval + x1 = InCurve ->Table16[j]; + x2 = InCurve ->Table16[j+1]; + + y1 = (cmsFloat64Number) (j * 65535.0) / (InCurve ->nEntries - 1); + y2 = (cmsFloat64Number) ((j+1) * 65535.0 ) / (InCurve ->nEntries - 1); + + // If collapsed, then use any + if (x1 == x2) { + + out ->Table16[i] = _cmsQuickSaturateWord(Ascending ? y2 : y1); + continue; + + } else { + + // Interpolate + a = (y2 - y1) / (x2 - x1); + b = y2 - a * x2; + } + } + + out ->Table16[i] = _cmsQuickSaturateWord(a* y + b); + } + + + return out; +} + +// Reverse a gamma table +cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma) +{ + _cmsAssert(InGamma != NULL); + + return cmsReverseToneCurveEx(4096, InGamma); +} + +// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite +// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press. +// +// Smoothing and interpolation with second differences. +// +// Input: weights (w), data (y): vector from 1 to m. +// Input: smoothing parameter (lambda), length (m). +// Output: smoothed vector (z): vector from 1 to m. + +static +cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[], + cmsFloat32Number z[], cmsFloat32Number lambda, int m) +{ + int i, i1, i2; + cmsFloat32Number *c, *d, *e; + cmsBool st; + + + c = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + d = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + e = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + + if (c != NULL && d != NULL && e != NULL) { + + + d[1] = w[1] + lambda; + c[1] = -2 * lambda / d[1]; + e[1] = lambda /d[1]; + z[1] = w[1] * y[1]; + d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1]; + c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2]; + e[2] = lambda / d[2]; + z[2] = w[2] * y[2] - c[1] * z[1]; + + for (i = 3; i < m - 1; i++) { + i1 = i - 1; i2 = i - 2; + d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i]; + e[i] = lambda / d[i]; + z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2]; + } + + i1 = m - 2; i2 = m - 3; + + d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1]; + z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2]; + i1 = m - 1; i2 = m - 2; + + d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m]; + z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m]; + + for (i = m - 2; 1<= i; i--) + z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2]; + + st = TRUE; + } + else st = FALSE; + + if (c != NULL) _cmsFree(ContextID, c); + if (d != NULL) _cmsFree(ContextID, d); + if (e != NULL) _cmsFree(ContextID, e); + + return st; +} + +// Smooths a curve sampled at regular intervals. +cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda) +{ + cmsBool SuccessStatus = TRUE; + cmsFloat32Number *w, *y, *z; + cmsUInt32Number i, nItems, Zeros, Poles; + cmsBool notCheck = FALSE; + + if (Tab != NULL && Tab->InterpParams != NULL) + { + cmsContext ContextID = Tab->InterpParams->ContextID; + + if (!cmsIsToneCurveLinear(Tab)) // Only non-linear curves need smoothing + { + nItems = Tab->nEntries; + if (nItems < MAX_NODES_IN_CURVE) + { + // Allocate one more item than needed + w = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number)); + y = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number)); + z = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number)); + + if (w != NULL && y != NULL && z != NULL) // Ensure no memory allocation failure + { + memset(w, 0, (nItems + 1) * sizeof(cmsFloat32Number)); + memset(y, 0, (nItems + 1) * sizeof(cmsFloat32Number)); + memset(z, 0, (nItems + 1) * sizeof(cmsFloat32Number)); + + for (i = 0; i < nItems; i++) + { + y[i + 1] = (cmsFloat32Number)Tab->Table16[i]; + w[i + 1] = 1.0; + } + + if (lambda < 0) + { + notCheck = TRUE; + lambda = -lambda; + } + + if (smooth2(ContextID, w, y, z, (cmsFloat32Number)lambda, (int)nItems)) + { + // Do some reality - checking... + + Zeros = Poles = 0; + for (i = nItems; i > 1; --i) + { + if (z[i] == 0.) Zeros++; + if (z[i] >= 65535.) Poles++; + if (z[i] < z[i - 1]) + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic."); + SuccessStatus = notCheck; + break; + } + } + + if (SuccessStatus && Zeros > (nItems / 3)) + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros."); + SuccessStatus = notCheck; + } + + if (SuccessStatus && Poles > (nItems / 3)) + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles."); + SuccessStatus = notCheck; + } + + if (SuccessStatus) // Seems ok + { + for (i = 0; i < nItems; i++) + { + // Clamp to cmsUInt16Number + Tab->Table16[i] = _cmsQuickSaturateWord(z[i + 1]); + } + } + } + else // Could not smooth + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Function smooth2 failed."); + SuccessStatus = FALSE; + } + } + else // One or more buffers could not be allocated + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Could not allocate memory."); + SuccessStatus = FALSE; + } + + if (z != NULL) + _cmsFree(ContextID, z); + + if (y != NULL) + _cmsFree(ContextID, y); + + if (w != NULL) + _cmsFree(ContextID, w); + } + else // too many items in the table + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Too many points."); + SuccessStatus = FALSE; + } + } + } + else // Tab parameter or Tab->InterpParams is NULL + { + // Can't signal an error here since the ContextID is not known at this point + SuccessStatus = FALSE; + } + + return SuccessStatus; +} + +// Is a table linear? Do not use parametric since we cannot guarantee some weird parameters resulting +// in a linear table. This way assures it is linear in 12 bits, which should be enough in most cases. +cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve) +{ + int i; + int diff; + + _cmsAssert(Curve != NULL); + + for (i=0; i < (int) Curve ->nEntries; i++) { + + diff = abs((int) Curve->Table16[i] - (int) _cmsQuantizeVal(i, Curve ->nEntries)); + if (diff > 0x0f) + return FALSE; + } + + return TRUE; +} + +// Same, but for monotonicity +cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t) +{ + cmsUInt32Number n; + int i, last; + cmsBool lDescending; + + _cmsAssert(t != NULL); + + // Degenerated curves are monotonic? Ok, let's pass them + n = t ->nEntries; + if (n < 2) return TRUE; + + // Curve direction + lDescending = cmsIsToneCurveDescending(t); + + if (lDescending) { + + last = t ->Table16[0]; + + for (i = 1; i < (int) n; i++) { + + if (t ->Table16[i] - last > 2) // We allow some ripple + return FALSE; + else + last = t ->Table16[i]; + + } + } + else { + + last = t ->Table16[n-1]; + + for (i = (int) n - 2; i >= 0; --i) { + + if (t ->Table16[i] - last > 2) + return FALSE; + else + last = t ->Table16[i]; + + } + } + + return TRUE; +} + +// Same, but for descending tables +cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + return t ->Table16[0] > t ->Table16[t ->nEntries-1]; +} + + +// Another info fn: is out gamma table multisegment? +cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + return t -> nSegments > 1; +} + +cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + if (t -> nSegments != 1) return 0; + return t ->Segments[0].Type; +} + +// We need accuracy this time +cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v) +{ + _cmsAssert(Curve != NULL); + + // Check for 16 bits table. If so, this is a limited-precision tone curve + if (Curve ->nSegments == 0) { + + cmsUInt16Number In, Out; + + In = (cmsUInt16Number) _cmsQuickSaturateWord(v * 65535.0); + Out = cmsEvalToneCurve16(Curve, In); + + return (cmsFloat32Number) (Out / 65535.0); + } + + return (cmsFloat32Number) EvalSegmentedFn(Curve, v); +} + +// We need xput over here +cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v) +{ + cmsUInt16Number out; + + _cmsAssert(Curve != NULL); + + Curve ->InterpParams ->Interpolation.Lerp16(&v, &out, Curve ->InterpParams); + return out; +} + + +// Least squares fitting. +// A mathematical procedure for finding the best-fitting curve to a given set of points by +// minimizing the sum of the squares of the offsets ("the residuals") of the points from the curve. +// The sum of the squares of the offsets is used instead of the offset absolute values because +// this allows the residuals to be treated as a continuous differentiable quantity. +// +// y = f(x) = x ^ g +// +// R = (yi - (xi^g)) +// R2 = (yi - (xi^g))2 +// SUM R2 = SUM (yi - (xi^g))2 +// +// dR2/dg = -2 SUM x^g log(x)(y - x^g) +// solving for dR2/dg = 0 +// +// g = 1/n * SUM(log(y) / log(x)) + +cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision) +{ + cmsFloat64Number gamma, sum, sum2; + cmsFloat64Number n, x, y, Std; + cmsUInt32Number i; + + _cmsAssert(t != NULL); + + sum = sum2 = n = 0; + + // Excluding endpoints + for (i=1; i < (MAX_NODES_IN_CURVE-1); i++) { + + x = (cmsFloat64Number) i / (MAX_NODES_IN_CURVE-1); + y = (cmsFloat64Number) cmsEvalToneCurveFloat(t, (cmsFloat32Number) x); + + // Avoid 7% on lower part to prevent + // artifacts due to linear ramps + + if (y > 0. && y < 1. && x > 0.07) { + + gamma = log(y) / log(x); + sum += gamma; + sum2 += gamma * gamma; + n++; + } + } + + // Take a look on SD to see if gamma isn't exponential at all + Std = sqrt((n * sum2 - sum * sum) / (n*(n-1))); + + if (Std > Precision) + return -1.0; + + return (sum / n); // The mean +} + + +// Retrieve parameters on one-segment tone curves + +cmsFloat64Number* CMSEXPORT cmsGetToneCurveParams(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + if (t->nSegments != 1) return NULL; + return t->Segments[0].Params; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsgmt.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsgmt.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsgmt.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsgmt.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,619 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// Auxiliary: append a Lab identity after the given sequence of profiles +// and return the transform. Lab profile is closed, rest of profiles are kept open. +cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsHTRANSFORM xform; + cmsHPROFILE hLab; + cmsHPROFILE ProfileList[256]; + cmsBool BPCList[256]; + cmsFloat64Number AdaptationList[256]; + cmsUInt32Number IntentList[256]; + cmsUInt32Number i; + + // This is a rather big number and there is no need of dynamic memory + // since we are adding a profile, 254 + 1 = 255 and this is the limit + if (nProfiles > 254) return NULL; + + // The output space + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return NULL; + + // Create a copy of parameters + for (i=0; i < nProfiles; i++) { + + ProfileList[i] = hProfiles[i]; + BPCList[i] = BPC[i]; + AdaptationList[i] = AdaptationStates[i]; + IntentList[i] = Intents[i]; + } + + // Place Lab identity at chain's end. + ProfileList[nProfiles] = hLab; + BPCList[nProfiles] = 0; + AdaptationList[nProfiles] = 1.0; + IntentList[nProfiles] = INTENT_RELATIVE_COLORIMETRIC; + + // Create the transform + xform = cmsCreateExtendedTransform(ContextID, nProfiles + 1, ProfileList, + BPCList, + IntentList, + AdaptationList, + NULL, 0, + InputFormat, + OutputFormat, + dwFlags); + + cmsCloseProfile(hLab); + + return xform; +} + + +// Compute K -> L* relationship. Flags may include black point compensation. In this case, +// the relationship is assumed from the profile with BPC to a black point zero. +static +cmsToneCurve* ComputeKToLstar(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsToneCurve* out = NULL; + cmsUInt32Number i; + cmsHTRANSFORM xform; + cmsCIELab Lab; + cmsFloat32Number cmyk[4]; + cmsFloat32Number* SampledPoints; + + xform = _cmsChain2Lab(ContextID, nProfiles, TYPE_CMYK_FLT, TYPE_Lab_DBL, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (xform == NULL) return NULL; + + SampledPoints = (cmsFloat32Number*) _cmsCalloc(ContextID, nPoints, sizeof(cmsFloat32Number)); + if (SampledPoints == NULL) goto Error; + + for (i=0; i < nPoints; i++) { + + cmyk[0] = 0; + cmyk[1] = 0; + cmyk[2] = 0; + cmyk[3] = (cmsFloat32Number) ((i * 100.0) / (nPoints-1)); + + cmsDoTransform(xform, cmyk, &Lab, 1); + SampledPoints[i]= (cmsFloat32Number) (1.0 - Lab.L / 100.0); // Negate K for easier operation + } + + out = cmsBuildTabulatedToneCurveFloat(ContextID, nPoints, SampledPoints); + +Error: + + cmsDeleteTransform(xform); + if (SampledPoints) _cmsFree(ContextID, SampledPoints); + + return out; +} + + +// Compute Black tone curve on a CMYK -> CMYK transform. This is done by +// using the proof direction on both profiles to find K->L* relationship +// then joining both curves. dwFlags may include black point compensation. +cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsToneCurve *in, *out, *KTone; + + // Make sure CMYK -> CMYK + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + cmsGetColorSpace(hProfiles[nProfiles-1])!= cmsSigCmykData) return NULL; + + + // Make sure last is an output profile + if (cmsGetDeviceClass(hProfiles[nProfiles - 1]) != cmsSigOutputClass) return NULL; + + // Create individual curves. BPC works also as each K to L* is + // computed as a BPC to zero black point in case of L* + in = ComputeKToLstar(ContextID, nPoints, nProfiles - 1, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (in == NULL) return NULL; + + out = ComputeKToLstar(ContextID, nPoints, 1, + Intents + (nProfiles - 1), + &hProfiles [nProfiles - 1], + BPC + (nProfiles - 1), + AdaptationStates + (nProfiles - 1), + dwFlags); + if (out == NULL) { + cmsFreeToneCurve(in); + return NULL; + } + + // Build the relationship. This effectively limits the maximum accuracy to 16 bits, but + // since this is used on black-preserving LUTs, we are not losing accuracy in any case + KTone = cmsJoinToneCurve(ContextID, in, out, nPoints); + + // Get rid of components + cmsFreeToneCurve(in); cmsFreeToneCurve(out); + + // Something went wrong... + if (KTone == NULL) return NULL; + + // Make sure it is monotonic + if (!cmsIsToneCurveMonotonic(KTone)) { + cmsFreeToneCurve(KTone); + return NULL; + } + + return KTone; +} + + +// Gamut LUT Creation ----------------------------------------------------------------------------------------- + +// Used by gamut & softproofing + +typedef struct { + + cmsHTRANSFORM hInput; // From whatever input color space. 16 bits to DBL + cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back + cmsFloat64Number Thereshold; // The thereshold after which is considered out of gamut + + } GAMUTCHAIN; + +// This sampler does compute gamut boundaries by comparing original +// values with a transform going back and forth. Values above ERR_THERESHOLD +// of maximum are considered out of gamut. + +#define ERR_THERESHOLD 5 + + +static +int GamutSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) +{ + GAMUTCHAIN* t = (GAMUTCHAIN* ) Cargo; + cmsCIELab LabIn1, LabOut1; + cmsCIELab LabIn2, LabOut2; + cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS]; + cmsFloat64Number dE1, dE2, ErrorRatio; + + // Assume in-gamut by default. + ErrorRatio = 1.0; + + // Convert input to Lab + cmsDoTransform(t -> hInput, In, &LabIn1, 1); + + // converts from PCS to colorant. This always + // does return in-gamut values, + cmsDoTransform(t -> hForward, &LabIn1, Proof, 1); + + // Now, do the inverse, from colorant to PCS. + cmsDoTransform(t -> hReverse, Proof, &LabOut1, 1); + + memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab)); + + // Try again, but this time taking Check as input + cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1); + cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1); + + // Take difference of direct value + dE1 = cmsDeltaE(&LabIn1, &LabOut1); + + // Take difference of converted value + dE2 = cmsDeltaE(&LabIn2, &LabOut2); + + + // if dE1 is small and dE2 is small, value is likely to be in gamut + if (dE1 < t->Thereshold && dE2 < t->Thereshold) + Out[0] = 0; + else { + + // if dE1 is small and dE2 is big, undefined. Assume in gamut + if (dE1 < t->Thereshold && dE2 > t->Thereshold) + Out[0] = 0; + else + // dE1 is big and dE2 is small, clearly out of gamut + if (dE1 > t->Thereshold && dE2 < t->Thereshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5); + else { + + // dE1 is big and dE2 is also big, could be due to perceptual mapping + // so take error ratio + if (dE2 == 0.0) + ErrorRatio = dE1; + else + ErrorRatio = dE1 / dE2; + + if (ErrorRatio > t->Thereshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5); + else + Out[0] = 0; + } + } + + + return TRUE; +} + +// Does compute a gamut LUT going back and forth across pcs -> relativ. colorimetric intent -> pcs +// the dE obtained is then annotated on the LUT. Values truly out of gamut are clipped to dE = 0xFFFE +// and values changed are supposed to be handled by any gamut remapping, so, are out of gamut as well. +// +// **WARNING: This algorithm does assume that gamut remapping algorithms does NOT move in-gamut colors, +// of course, many perceptual and saturation intents does not work in such way, but relativ. ones should. + +cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number nGamutPCSposition, + cmsHPROFILE hGamut) +{ + cmsHPROFILE hLab; + cmsPipeline* Gamut; + cmsStage* CLUT; + cmsUInt32Number dwFormat; + GAMUTCHAIN Chain; + cmsUInt32Number nChannels, nGridpoints; + cmsColorSpaceSignature ColorSpace; + cmsUInt32Number i; + cmsHPROFILE ProfileList[256]; + cmsBool BPCList[256]; + cmsFloat64Number AdaptationList[256]; + cmsUInt32Number IntentList[256]; + + memset(&Chain, 0, sizeof(GAMUTCHAIN)); + + + if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition); + return NULL; + } + + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return NULL; + + + // The figure of merit. On matrix-shaper profiles, should be almost zero as + // the conversion is pretty exact. On LUT based profiles, different resolutions + // of input and output CLUT may result in differences. + + if (cmsIsMatrixShaper(hGamut)) { + + Chain.Thereshold = 1.0; + } + else { + Chain.Thereshold = ERR_THERESHOLD; + } + + + // Create a copy of parameters + for (i=0; i < nGamutPCSposition; i++) { + ProfileList[i] = hProfiles[i]; + BPCList[i] = BPC[i]; + AdaptationList[i] = AdaptationStates[i]; + IntentList[i] = Intents[i]; + } + + // Fill Lab identity + ProfileList[nGamutPCSposition] = hLab; + BPCList[nGamutPCSposition] = 0; + AdaptationList[nGamutPCSposition] = 1.0; + IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC; + + + ColorSpace = cmsGetColorSpace(hGamut); + + nChannels = cmsChannelsOf(ColorSpace); + nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); + dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); + + // 16 bits to Lab double + Chain.hInput = cmsCreateExtendedTransform(ContextID, + nGamutPCSposition + 1, + ProfileList, + BPCList, + IntentList, + AdaptationList, + NULL, 0, + dwFormat, TYPE_Lab_DBL, + cmsFLAGS_NOCACHE); + + + // Does create the forward step. Lab double to device + dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); + Chain.hForward = cmsCreateTransformTHR(ContextID, + hLab, TYPE_Lab_DBL, + hGamut, dwFormat, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE); + + // Does create the backwards step + Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat, + hLab, TYPE_Lab_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE); + + + // All ok? + if (Chain.hInput && Chain.hForward && Chain.hReverse) { + + // Go on, try to compute gamut LUT from PCS. This consist on a single channel containing + // dE when doing a transform back and forth on the colorimetric intent. + + Gamut = cmsPipelineAlloc(ContextID, 3, 1); + if (Gamut != NULL) { + + CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL); + if (!cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT)) { + cmsPipelineFree(Gamut); + Gamut = NULL; + } + else { + cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0); + } + } + } + else + Gamut = NULL; // Didn't work... + + // Free all needed stuff. + if (Chain.hInput) cmsDeleteTransform(Chain.hInput); + if (Chain.hForward) cmsDeleteTransform(Chain.hForward); + if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse); + if (hLab) cmsCloseProfile(hLab); + + // And return computed hull + return Gamut; +} + +// Total Area Coverage estimation ---------------------------------------------------------------- + +typedef struct { + cmsUInt32Number nOutputChans; + cmsHTRANSFORM hRoundTrip; + cmsFloat32Number MaxTAC; + cmsFloat32Number MaxInput[cmsMAXCHANNELS]; + +} cmsTACestimator; + + +// This callback just accounts the maximum ink dropped in the given node. It does not populate any +// memory, as the destination table is NULL. Its only purpose it to know the global maximum. +static +int EstimateTAC(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void * Cargo) +{ + cmsTACestimator* bp = (cmsTACestimator*) Cargo; + cmsFloat32Number RoundTrip[cmsMAXCHANNELS]; + cmsUInt32Number i; + cmsFloat32Number Sum; + + + // Evaluate the xform + cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1); + + // All all amounts of ink + for (Sum=0, i=0; i < bp ->nOutputChans; i++) + Sum += RoundTrip[i]; + + // If above maximum, keep track of input values + if (Sum > bp ->MaxTAC) { + + bp ->MaxTAC = Sum; + + for (i=0; i < bp ->nOutputChans; i++) { + bp ->MaxInput[i] = In[i]; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(Out); +} + + +// Detect Total area coverage of the profile +cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile) +{ + cmsTACestimator bp; + cmsUInt32Number dwFormatter; + cmsUInt32Number GridPoints[MAX_INPUT_DIMENSIONS]; + cmsHPROFILE hLab; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + // TAC only works on output profiles + if (cmsGetDeviceClass(hProfile) != cmsSigOutputClass) { + return 0; + } + + // Create a fake formatter for result + dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE); + + bp.nOutputChans = T_CHANNELS(dwFormatter); + bp.MaxTAC = 0; // Initial TAC is 0 + + // for safety + if (bp.nOutputChans >= cmsMAXCHANNELS) return 0; + + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return 0; + // Setup a roundtrip on perceptual intent in output profile for TAC estimation + bp.hRoundTrip = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_16, + hProfile, dwFormatter, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); + + cmsCloseProfile(hLab); + if (bp.hRoundTrip == NULL) return 0; + + // For L* we only need black and white. For C* we need many points + GridPoints[0] = 6; + GridPoints[1] = 74; + GridPoints[2] = 74; + + + if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) { + bp.MaxTAC = 0; + } + + cmsDeleteTransform(bp.hRoundTrip); + + // Results in % + return bp.MaxTAC; +} + + +// Carefully, clamp on CIELab space. + +cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, + double amax, double amin, + double bmax, double bmin) +{ + + // Whole Luma surface to zero + + if (Lab -> L < 0) { + + Lab-> L = Lab->a = Lab-> b = 0.0; + return FALSE; + } + + // Clamp white, DISCARD HIGHLIGHTS. This is done + // in such way because icc spec doesn't allow the + // use of L>100 as a highlight means. + + if (Lab->L > 100) + Lab -> L = 100; + + // Check out gamut prism, on a, b faces + + if (Lab -> a < amin || Lab->a > amax|| + Lab -> b < bmin || Lab->b > bmax) { + + cmsCIELCh LCh; + double h, slope; + + // Falls outside a, b limits. Transports to LCh space, + // and then do the clipping + + + if (Lab -> a == 0.0) { // Is hue exactly 90? + + // atan will not work, so clamp here + Lab -> b = Lab->b < 0 ? bmin : bmax; + return TRUE; + } + + cmsLab2LCh(&LCh, Lab); + + slope = Lab -> b / Lab -> a; + h = LCh.h; + + // There are 4 zones + + if ((h >= 0. && h < 45.) || + (h >= 315 && h <= 360.)) { + + // clip by amax + Lab -> a = amax; + Lab -> b = amax * slope; + } + else + if (h >= 45. && h < 135.) + { + // clip by bmax + Lab -> b = bmax; + Lab -> a = bmax / slope; + } + else + if (h >= 135. && h < 225.) { + // clip by amin + Lab -> a = amin; + Lab -> b = amin * slope; + + } + else + if (h >= 225. && h < 315.) { + // clip by bmin + Lab -> b = bmin; + Lab -> a = bmin / slope; + } + else { + cmsSignalError(0, cmsERROR_RANGE, "Invalid angle"); + return FALSE; + } + + } + + return TRUE; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmshalf.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmshalf.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmshalf.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmshalf.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,564 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +// +#include "lcms2_internal.h" + +#ifndef CMS_NO_HALF_SUPPORT + +// This code is inspired in the paper "Fast Half Float Conversions" +// by Jeroen van der Zijp + +static const cmsUInt32Number Mantissa[2048] = { + +0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000, +0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000, +0x35400000, 0x35500000, 0x35600000, 0x35700000, 0x35800000, 0x35880000, +0x35900000, 0x35980000, 0x35a00000, 0x35a80000, 0x35b00000, 0x35b80000, +0x35c00000, 0x35c80000, 0x35d00000, 0x35d80000, 0x35e00000, 0x35e80000, +0x35f00000, 0x35f80000, 0x36000000, 0x36040000, 0x36080000, 0x360c0000, +0x36100000, 0x36140000, 0x36180000, 0x361c0000, 0x36200000, 0x36240000, +0x36280000, 0x362c0000, 0x36300000, 0x36340000, 0x36380000, 0x363c0000, +0x36400000, 0x36440000, 0x36480000, 0x364c0000, 0x36500000, 0x36540000, +0x36580000, 0x365c0000, 0x36600000, 0x36640000, 0x36680000, 0x366c0000, +0x36700000, 0x36740000, 0x36780000, 0x367c0000, 0x36800000, 0x36820000, +0x36840000, 0x36860000, 0x36880000, 0x368a0000, 0x368c0000, 0x368e0000, +0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369a0000, +0x369c0000, 0x369e0000, 0x36a00000, 0x36a20000, 0x36a40000, 0x36a60000, +0x36a80000, 0x36aa0000, 0x36ac0000, 0x36ae0000, 0x36b00000, 0x36b20000, +0x36b40000, 0x36b60000, 0x36b80000, 0x36ba0000, 0x36bc0000, 0x36be0000, +0x36c00000, 0x36c20000, 0x36c40000, 0x36c60000, 0x36c80000, 0x36ca0000, +0x36cc0000, 0x36ce0000, 0x36d00000, 0x36d20000, 0x36d40000, 0x36d60000, +0x36d80000, 0x36da0000, 0x36dc0000, 0x36de0000, 0x36e00000, 0x36e20000, +0x36e40000, 0x36e60000, 0x36e80000, 0x36ea0000, 0x36ec0000, 0x36ee0000, +0x36f00000, 0x36f20000, 0x36f40000, 0x36f60000, 0x36f80000, 0x36fa0000, +0x36fc0000, 0x36fe0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000, +0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000, +0x370a0000, 0x370b0000, 0x370c0000, 0x370d0000, 0x370e0000, 0x370f0000, +0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000, +0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371a0000, 0x371b0000, +0x371c0000, 0x371d0000, 0x371e0000, 0x371f0000, 0x37200000, 0x37210000, +0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000, +0x37280000, 0x37290000, 0x372a0000, 0x372b0000, 0x372c0000, 0x372d0000, +0x372e0000, 0x372f0000, 0x37300000, 0x37310000, 0x37320000, 0x37330000, +0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000, +0x373a0000, 0x373b0000, 0x373c0000, 0x373d0000, 0x373e0000, 0x373f0000, +0x37400000, 0x37410000, 0x37420000, 0x37430000, 0x37440000, 0x37450000, +0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374a0000, 0x374b0000, +0x374c0000, 0x374d0000, 0x374e0000, 0x374f0000, 0x37500000, 0x37510000, +0x37520000, 0x37530000, 0x37540000, 0x37550000, 0x37560000, 0x37570000, +0x37580000, 0x37590000, 0x375a0000, 0x375b0000, 0x375c0000, 0x375d0000, +0x375e0000, 0x375f0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000, +0x37640000, 0x37650000, 0x37660000, 0x37670000, 0x37680000, 0x37690000, +0x376a0000, 0x376b0000, 0x376c0000, 0x376d0000, 0x376e0000, 0x376f0000, +0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000, +0x37760000, 0x37770000, 0x37780000, 0x37790000, 0x377a0000, 0x377b0000, +0x377c0000, 0x377d0000, 0x377e0000, 0x377f0000, 0x37800000, 0x37808000, +0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000, +0x37840000, 0x37848000, 0x37850000, 0x37858000, 0x37860000, 0x37868000, +0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000, +0x378a0000, 0x378a8000, 0x378b0000, 0x378b8000, 0x378c0000, 0x378c8000, +0x378d0000, 0x378d8000, 0x378e0000, 0x378e8000, 0x378f0000, 0x378f8000, +0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000, +0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000, +0x37960000, 0x37968000, 0x37970000, 0x37978000, 0x37980000, 0x37988000, +0x37990000, 0x37998000, 0x379a0000, 0x379a8000, 0x379b0000, 0x379b8000, +0x379c0000, 0x379c8000, 0x379d0000, 0x379d8000, 0x379e0000, 0x379e8000, +0x379f0000, 0x379f8000, 0x37a00000, 0x37a08000, 0x37a10000, 0x37a18000, +0x37a20000, 0x37a28000, 0x37a30000, 0x37a38000, 0x37a40000, 0x37a48000, +0x37a50000, 0x37a58000, 0x37a60000, 0x37a68000, 0x37a70000, 0x37a78000, +0x37a80000, 0x37a88000, 0x37a90000, 0x37a98000, 0x37aa0000, 0x37aa8000, +0x37ab0000, 0x37ab8000, 0x37ac0000, 0x37ac8000, 0x37ad0000, 0x37ad8000, +0x37ae0000, 0x37ae8000, 0x37af0000, 0x37af8000, 0x37b00000, 0x37b08000, +0x37b10000, 0x37b18000, 0x37b20000, 0x37b28000, 0x37b30000, 0x37b38000, +0x37b40000, 0x37b48000, 0x37b50000, 0x37b58000, 0x37b60000, 0x37b68000, +0x37b70000, 0x37b78000, 0x37b80000, 0x37b88000, 0x37b90000, 0x37b98000, +0x37ba0000, 0x37ba8000, 0x37bb0000, 0x37bb8000, 0x37bc0000, 0x37bc8000, +0x37bd0000, 0x37bd8000, 0x37be0000, 0x37be8000, 0x37bf0000, 0x37bf8000, +0x37c00000, 0x37c08000, 0x37c10000, 0x37c18000, 0x37c20000, 0x37c28000, +0x37c30000, 0x37c38000, 0x37c40000, 0x37c48000, 0x37c50000, 0x37c58000, +0x37c60000, 0x37c68000, 0x37c70000, 0x37c78000, 0x37c80000, 0x37c88000, +0x37c90000, 0x37c98000, 0x37ca0000, 0x37ca8000, 0x37cb0000, 0x37cb8000, +0x37cc0000, 0x37cc8000, 0x37cd0000, 0x37cd8000, 0x37ce0000, 0x37ce8000, +0x37cf0000, 0x37cf8000, 0x37d00000, 0x37d08000, 0x37d10000, 0x37d18000, +0x37d20000, 0x37d28000, 0x37d30000, 0x37d38000, 0x37d40000, 0x37d48000, +0x37d50000, 0x37d58000, 0x37d60000, 0x37d68000, 0x37d70000, 0x37d78000, +0x37d80000, 0x37d88000, 0x37d90000, 0x37d98000, 0x37da0000, 0x37da8000, +0x37db0000, 0x37db8000, 0x37dc0000, 0x37dc8000, 0x37dd0000, 0x37dd8000, +0x37de0000, 0x37de8000, 0x37df0000, 0x37df8000, 0x37e00000, 0x37e08000, +0x37e10000, 0x37e18000, 0x37e20000, 0x37e28000, 0x37e30000, 0x37e38000, +0x37e40000, 0x37e48000, 0x37e50000, 0x37e58000, 0x37e60000, 0x37e68000, +0x37e70000, 0x37e78000, 0x37e80000, 0x37e88000, 0x37e90000, 0x37e98000, +0x37ea0000, 0x37ea8000, 0x37eb0000, 0x37eb8000, 0x37ec0000, 0x37ec8000, +0x37ed0000, 0x37ed8000, 0x37ee0000, 0x37ee8000, 0x37ef0000, 0x37ef8000, +0x37f00000, 0x37f08000, 0x37f10000, 0x37f18000, 0x37f20000, 0x37f28000, +0x37f30000, 0x37f38000, 0x37f40000, 0x37f48000, 0x37f50000, 0x37f58000, +0x37f60000, 0x37f68000, 0x37f70000, 0x37f78000, 0x37f80000, 0x37f88000, +0x37f90000, 0x37f98000, 0x37fa0000, 0x37fa8000, 0x37fb0000, 0x37fb8000, +0x37fc0000, 0x37fc8000, 0x37fd0000, 0x37fd8000, 0x37fe0000, 0x37fe8000, +0x37ff0000, 0x37ff8000, 0x38000000, 0x38004000, 0x38008000, 0x3800c000, +0x38010000, 0x38014000, 0x38018000, 0x3801c000, 0x38020000, 0x38024000, +0x38028000, 0x3802c000, 0x38030000, 0x38034000, 0x38038000, 0x3803c000, +0x38040000, 0x38044000, 0x38048000, 0x3804c000, 0x38050000, 0x38054000, +0x38058000, 0x3805c000, 0x38060000, 0x38064000, 0x38068000, 0x3806c000, +0x38070000, 0x38074000, 0x38078000, 0x3807c000, 0x38080000, 0x38084000, +0x38088000, 0x3808c000, 0x38090000, 0x38094000, 0x38098000, 0x3809c000, +0x380a0000, 0x380a4000, 0x380a8000, 0x380ac000, 0x380b0000, 0x380b4000, +0x380b8000, 0x380bc000, 0x380c0000, 0x380c4000, 0x380c8000, 0x380cc000, +0x380d0000, 0x380d4000, 0x380d8000, 0x380dc000, 0x380e0000, 0x380e4000, +0x380e8000, 0x380ec000, 0x380f0000, 0x380f4000, 0x380f8000, 0x380fc000, +0x38100000, 0x38104000, 0x38108000, 0x3810c000, 0x38110000, 0x38114000, +0x38118000, 0x3811c000, 0x38120000, 0x38124000, 0x38128000, 0x3812c000, +0x38130000, 0x38134000, 0x38138000, 0x3813c000, 0x38140000, 0x38144000, +0x38148000, 0x3814c000, 0x38150000, 0x38154000, 0x38158000, 0x3815c000, +0x38160000, 0x38164000, 0x38168000, 0x3816c000, 0x38170000, 0x38174000, +0x38178000, 0x3817c000, 0x38180000, 0x38184000, 0x38188000, 0x3818c000, +0x38190000, 0x38194000, 0x38198000, 0x3819c000, 0x381a0000, 0x381a4000, +0x381a8000, 0x381ac000, 0x381b0000, 0x381b4000, 0x381b8000, 0x381bc000, +0x381c0000, 0x381c4000, 0x381c8000, 0x381cc000, 0x381d0000, 0x381d4000, +0x381d8000, 0x381dc000, 0x381e0000, 0x381e4000, 0x381e8000, 0x381ec000, +0x381f0000, 0x381f4000, 0x381f8000, 0x381fc000, 0x38200000, 0x38204000, +0x38208000, 0x3820c000, 0x38210000, 0x38214000, 0x38218000, 0x3821c000, +0x38220000, 0x38224000, 0x38228000, 0x3822c000, 0x38230000, 0x38234000, +0x38238000, 0x3823c000, 0x38240000, 0x38244000, 0x38248000, 0x3824c000, +0x38250000, 0x38254000, 0x38258000, 0x3825c000, 0x38260000, 0x38264000, +0x38268000, 0x3826c000, 0x38270000, 0x38274000, 0x38278000, 0x3827c000, +0x38280000, 0x38284000, 0x38288000, 0x3828c000, 0x38290000, 0x38294000, +0x38298000, 0x3829c000, 0x382a0000, 0x382a4000, 0x382a8000, 0x382ac000, +0x382b0000, 0x382b4000, 0x382b8000, 0x382bc000, 0x382c0000, 0x382c4000, +0x382c8000, 0x382cc000, 0x382d0000, 0x382d4000, 0x382d8000, 0x382dc000, +0x382e0000, 0x382e4000, 0x382e8000, 0x382ec000, 0x382f0000, 0x382f4000, +0x382f8000, 0x382fc000, 0x38300000, 0x38304000, 0x38308000, 0x3830c000, +0x38310000, 0x38314000, 0x38318000, 0x3831c000, 0x38320000, 0x38324000, +0x38328000, 0x3832c000, 0x38330000, 0x38334000, 0x38338000, 0x3833c000, +0x38340000, 0x38344000, 0x38348000, 0x3834c000, 0x38350000, 0x38354000, +0x38358000, 0x3835c000, 0x38360000, 0x38364000, 0x38368000, 0x3836c000, +0x38370000, 0x38374000, 0x38378000, 0x3837c000, 0x38380000, 0x38384000, +0x38388000, 0x3838c000, 0x38390000, 0x38394000, 0x38398000, 0x3839c000, +0x383a0000, 0x383a4000, 0x383a8000, 0x383ac000, 0x383b0000, 0x383b4000, +0x383b8000, 0x383bc000, 0x383c0000, 0x383c4000, 0x383c8000, 0x383cc000, +0x383d0000, 0x383d4000, 0x383d8000, 0x383dc000, 0x383e0000, 0x383e4000, +0x383e8000, 0x383ec000, 0x383f0000, 0x383f4000, 0x383f8000, 0x383fc000, +0x38400000, 0x38404000, 0x38408000, 0x3840c000, 0x38410000, 0x38414000, +0x38418000, 0x3841c000, 0x38420000, 0x38424000, 0x38428000, 0x3842c000, +0x38430000, 0x38434000, 0x38438000, 0x3843c000, 0x38440000, 0x38444000, +0x38448000, 0x3844c000, 0x38450000, 0x38454000, 0x38458000, 0x3845c000, +0x38460000, 0x38464000, 0x38468000, 0x3846c000, 0x38470000, 0x38474000, +0x38478000, 0x3847c000, 0x38480000, 0x38484000, 0x38488000, 0x3848c000, +0x38490000, 0x38494000, 0x38498000, 0x3849c000, 0x384a0000, 0x384a4000, +0x384a8000, 0x384ac000, 0x384b0000, 0x384b4000, 0x384b8000, 0x384bc000, +0x384c0000, 0x384c4000, 0x384c8000, 0x384cc000, 0x384d0000, 0x384d4000, +0x384d8000, 0x384dc000, 0x384e0000, 0x384e4000, 0x384e8000, 0x384ec000, +0x384f0000, 0x384f4000, 0x384f8000, 0x384fc000, 0x38500000, 0x38504000, +0x38508000, 0x3850c000, 0x38510000, 0x38514000, 0x38518000, 0x3851c000, +0x38520000, 0x38524000, 0x38528000, 0x3852c000, 0x38530000, 0x38534000, +0x38538000, 0x3853c000, 0x38540000, 0x38544000, 0x38548000, 0x3854c000, +0x38550000, 0x38554000, 0x38558000, 0x3855c000, 0x38560000, 0x38564000, +0x38568000, 0x3856c000, 0x38570000, 0x38574000, 0x38578000, 0x3857c000, +0x38580000, 0x38584000, 0x38588000, 0x3858c000, 0x38590000, 0x38594000, +0x38598000, 0x3859c000, 0x385a0000, 0x385a4000, 0x385a8000, 0x385ac000, +0x385b0000, 0x385b4000, 0x385b8000, 0x385bc000, 0x385c0000, 0x385c4000, +0x385c8000, 0x385cc000, 0x385d0000, 0x385d4000, 0x385d8000, 0x385dc000, +0x385e0000, 0x385e4000, 0x385e8000, 0x385ec000, 0x385f0000, 0x385f4000, +0x385f8000, 0x385fc000, 0x38600000, 0x38604000, 0x38608000, 0x3860c000, +0x38610000, 0x38614000, 0x38618000, 0x3861c000, 0x38620000, 0x38624000, +0x38628000, 0x3862c000, 0x38630000, 0x38634000, 0x38638000, 0x3863c000, +0x38640000, 0x38644000, 0x38648000, 0x3864c000, 0x38650000, 0x38654000, +0x38658000, 0x3865c000, 0x38660000, 0x38664000, 0x38668000, 0x3866c000, +0x38670000, 0x38674000, 0x38678000, 0x3867c000, 0x38680000, 0x38684000, +0x38688000, 0x3868c000, 0x38690000, 0x38694000, 0x38698000, 0x3869c000, +0x386a0000, 0x386a4000, 0x386a8000, 0x386ac000, 0x386b0000, 0x386b4000, +0x386b8000, 0x386bc000, 0x386c0000, 0x386c4000, 0x386c8000, 0x386cc000, +0x386d0000, 0x386d4000, 0x386d8000, 0x386dc000, 0x386e0000, 0x386e4000, +0x386e8000, 0x386ec000, 0x386f0000, 0x386f4000, 0x386f8000, 0x386fc000, +0x38700000, 0x38704000, 0x38708000, 0x3870c000, 0x38710000, 0x38714000, +0x38718000, 0x3871c000, 0x38720000, 0x38724000, 0x38728000, 0x3872c000, +0x38730000, 0x38734000, 0x38738000, 0x3873c000, 0x38740000, 0x38744000, +0x38748000, 0x3874c000, 0x38750000, 0x38754000, 0x38758000, 0x3875c000, +0x38760000, 0x38764000, 0x38768000, 0x3876c000, 0x38770000, 0x38774000, +0x38778000, 0x3877c000, 0x38780000, 0x38784000, 0x38788000, 0x3878c000, +0x38790000, 0x38794000, 0x38798000, 0x3879c000, 0x387a0000, 0x387a4000, +0x387a8000, 0x387ac000, 0x387b0000, 0x387b4000, 0x387b8000, 0x387bc000, +0x387c0000, 0x387c4000, 0x387c8000, 0x387cc000, 0x387d0000, 0x387d4000, +0x387d8000, 0x387dc000, 0x387e0000, 0x387e4000, 0x387e8000, 0x387ec000, +0x387f0000, 0x387f4000, 0x387f8000, 0x387fc000, 0x38000000, 0x38002000, +0x38004000, 0x38006000, 0x38008000, 0x3800a000, 0x3800c000, 0x3800e000, +0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801a000, +0x3801c000, 0x3801e000, 0x38020000, 0x38022000, 0x38024000, 0x38026000, +0x38028000, 0x3802a000, 0x3802c000, 0x3802e000, 0x38030000, 0x38032000, +0x38034000, 0x38036000, 0x38038000, 0x3803a000, 0x3803c000, 0x3803e000, +0x38040000, 0x38042000, 0x38044000, 0x38046000, 0x38048000, 0x3804a000, +0x3804c000, 0x3804e000, 0x38050000, 0x38052000, 0x38054000, 0x38056000, +0x38058000, 0x3805a000, 0x3805c000, 0x3805e000, 0x38060000, 0x38062000, +0x38064000, 0x38066000, 0x38068000, 0x3806a000, 0x3806c000, 0x3806e000, +0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807a000, +0x3807c000, 0x3807e000, 0x38080000, 0x38082000, 0x38084000, 0x38086000, +0x38088000, 0x3808a000, 0x3808c000, 0x3808e000, 0x38090000, 0x38092000, +0x38094000, 0x38096000, 0x38098000, 0x3809a000, 0x3809c000, 0x3809e000, +0x380a0000, 0x380a2000, 0x380a4000, 0x380a6000, 0x380a8000, 0x380aa000, +0x380ac000, 0x380ae000, 0x380b0000, 0x380b2000, 0x380b4000, 0x380b6000, +0x380b8000, 0x380ba000, 0x380bc000, 0x380be000, 0x380c0000, 0x380c2000, +0x380c4000, 0x380c6000, 0x380c8000, 0x380ca000, 0x380cc000, 0x380ce000, +0x380d0000, 0x380d2000, 0x380d4000, 0x380d6000, 0x380d8000, 0x380da000, +0x380dc000, 0x380de000, 0x380e0000, 0x380e2000, 0x380e4000, 0x380e6000, +0x380e8000, 0x380ea000, 0x380ec000, 0x380ee000, 0x380f0000, 0x380f2000, +0x380f4000, 0x380f6000, 0x380f8000, 0x380fa000, 0x380fc000, 0x380fe000, +0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810a000, +0x3810c000, 0x3810e000, 0x38110000, 0x38112000, 0x38114000, 0x38116000, +0x38118000, 0x3811a000, 0x3811c000, 0x3811e000, 0x38120000, 0x38122000, +0x38124000, 0x38126000, 0x38128000, 0x3812a000, 0x3812c000, 0x3812e000, +0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813a000, +0x3813c000, 0x3813e000, 0x38140000, 0x38142000, 0x38144000, 0x38146000, +0x38148000, 0x3814a000, 0x3814c000, 0x3814e000, 0x38150000, 0x38152000, +0x38154000, 0x38156000, 0x38158000, 0x3815a000, 0x3815c000, 0x3815e000, +0x38160000, 0x38162000, 0x38164000, 0x38166000, 0x38168000, 0x3816a000, +0x3816c000, 0x3816e000, 0x38170000, 0x38172000, 0x38174000, 0x38176000, +0x38178000, 0x3817a000, 0x3817c000, 0x3817e000, 0x38180000, 0x38182000, +0x38184000, 0x38186000, 0x38188000, 0x3818a000, 0x3818c000, 0x3818e000, +0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819a000, +0x3819c000, 0x3819e000, 0x381a0000, 0x381a2000, 0x381a4000, 0x381a6000, +0x381a8000, 0x381aa000, 0x381ac000, 0x381ae000, 0x381b0000, 0x381b2000, +0x381b4000, 0x381b6000, 0x381b8000, 0x381ba000, 0x381bc000, 0x381be000, +0x381c0000, 0x381c2000, 0x381c4000, 0x381c6000, 0x381c8000, 0x381ca000, +0x381cc000, 0x381ce000, 0x381d0000, 0x381d2000, 0x381d4000, 0x381d6000, +0x381d8000, 0x381da000, 0x381dc000, 0x381de000, 0x381e0000, 0x381e2000, +0x381e4000, 0x381e6000, 0x381e8000, 0x381ea000, 0x381ec000, 0x381ee000, +0x381f0000, 0x381f2000, 0x381f4000, 0x381f6000, 0x381f8000, 0x381fa000, +0x381fc000, 0x381fe000, 0x38200000, 0x38202000, 0x38204000, 0x38206000, +0x38208000, 0x3820a000, 0x3820c000, 0x3820e000, 0x38210000, 0x38212000, +0x38214000, 0x38216000, 0x38218000, 0x3821a000, 0x3821c000, 0x3821e000, +0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822a000, +0x3822c000, 0x3822e000, 0x38230000, 0x38232000, 0x38234000, 0x38236000, +0x38238000, 0x3823a000, 0x3823c000, 0x3823e000, 0x38240000, 0x38242000, +0x38244000, 0x38246000, 0x38248000, 0x3824a000, 0x3824c000, 0x3824e000, +0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825a000, +0x3825c000, 0x3825e000, 0x38260000, 0x38262000, 0x38264000, 0x38266000, +0x38268000, 0x3826a000, 0x3826c000, 0x3826e000, 0x38270000, 0x38272000, +0x38274000, 0x38276000, 0x38278000, 0x3827a000, 0x3827c000, 0x3827e000, +0x38280000, 0x38282000, 0x38284000, 0x38286000, 0x38288000, 0x3828a000, +0x3828c000, 0x3828e000, 0x38290000, 0x38292000, 0x38294000, 0x38296000, +0x38298000, 0x3829a000, 0x3829c000, 0x3829e000, 0x382a0000, 0x382a2000, +0x382a4000, 0x382a6000, 0x382a8000, 0x382aa000, 0x382ac000, 0x382ae000, +0x382b0000, 0x382b2000, 0x382b4000, 0x382b6000, 0x382b8000, 0x382ba000, +0x382bc000, 0x382be000, 0x382c0000, 0x382c2000, 0x382c4000, 0x382c6000, +0x382c8000, 0x382ca000, 0x382cc000, 0x382ce000, 0x382d0000, 0x382d2000, +0x382d4000, 0x382d6000, 0x382d8000, 0x382da000, 0x382dc000, 0x382de000, +0x382e0000, 0x382e2000, 0x382e4000, 0x382e6000, 0x382e8000, 0x382ea000, +0x382ec000, 0x382ee000, 0x382f0000, 0x382f2000, 0x382f4000, 0x382f6000, +0x382f8000, 0x382fa000, 0x382fc000, 0x382fe000, 0x38300000, 0x38302000, +0x38304000, 0x38306000, 0x38308000, 0x3830a000, 0x3830c000, 0x3830e000, +0x38310000, 0x38312000, 0x38314000, 0x38316000, 0x38318000, 0x3831a000, +0x3831c000, 0x3831e000, 0x38320000, 0x38322000, 0x38324000, 0x38326000, +0x38328000, 0x3832a000, 0x3832c000, 0x3832e000, 0x38330000, 0x38332000, +0x38334000, 0x38336000, 0x38338000, 0x3833a000, 0x3833c000, 0x3833e000, +0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834a000, +0x3834c000, 0x3834e000, 0x38350000, 0x38352000, 0x38354000, 0x38356000, +0x38358000, 0x3835a000, 0x3835c000, 0x3835e000, 0x38360000, 0x38362000, +0x38364000, 0x38366000, 0x38368000, 0x3836a000, 0x3836c000, 0x3836e000, +0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837a000, +0x3837c000, 0x3837e000, 0x38380000, 0x38382000, 0x38384000, 0x38386000, +0x38388000, 0x3838a000, 0x3838c000, 0x3838e000, 0x38390000, 0x38392000, +0x38394000, 0x38396000, 0x38398000, 0x3839a000, 0x3839c000, 0x3839e000, +0x383a0000, 0x383a2000, 0x383a4000, 0x383a6000, 0x383a8000, 0x383aa000, +0x383ac000, 0x383ae000, 0x383b0000, 0x383b2000, 0x383b4000, 0x383b6000, +0x383b8000, 0x383ba000, 0x383bc000, 0x383be000, 0x383c0000, 0x383c2000, +0x383c4000, 0x383c6000, 0x383c8000, 0x383ca000, 0x383cc000, 0x383ce000, +0x383d0000, 0x383d2000, 0x383d4000, 0x383d6000, 0x383d8000, 0x383da000, +0x383dc000, 0x383de000, 0x383e0000, 0x383e2000, 0x383e4000, 0x383e6000, +0x383e8000, 0x383ea000, 0x383ec000, 0x383ee000, 0x383f0000, 0x383f2000, +0x383f4000, 0x383f6000, 0x383f8000, 0x383fa000, 0x383fc000, 0x383fe000, +0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840a000, +0x3840c000, 0x3840e000, 0x38410000, 0x38412000, 0x38414000, 0x38416000, +0x38418000, 0x3841a000, 0x3841c000, 0x3841e000, 0x38420000, 0x38422000, +0x38424000, 0x38426000, 0x38428000, 0x3842a000, 0x3842c000, 0x3842e000, +0x38430000, 0x38432000, 0x38434000, 0x38436000, 0x38438000, 0x3843a000, +0x3843c000, 0x3843e000, 0x38440000, 0x38442000, 0x38444000, 0x38446000, +0x38448000, 0x3844a000, 0x3844c000, 0x3844e000, 0x38450000, 0x38452000, +0x38454000, 0x38456000, 0x38458000, 0x3845a000, 0x3845c000, 0x3845e000, +0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846a000, +0x3846c000, 0x3846e000, 0x38470000, 0x38472000, 0x38474000, 0x38476000, +0x38478000, 0x3847a000, 0x3847c000, 0x3847e000, 0x38480000, 0x38482000, +0x38484000, 0x38486000, 0x38488000, 0x3848a000, 0x3848c000, 0x3848e000, +0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849a000, +0x3849c000, 0x3849e000, 0x384a0000, 0x384a2000, 0x384a4000, 0x384a6000, +0x384a8000, 0x384aa000, 0x384ac000, 0x384ae000, 0x384b0000, 0x384b2000, +0x384b4000, 0x384b6000, 0x384b8000, 0x384ba000, 0x384bc000, 0x384be000, +0x384c0000, 0x384c2000, 0x384c4000, 0x384c6000, 0x384c8000, 0x384ca000, +0x384cc000, 0x384ce000, 0x384d0000, 0x384d2000, 0x384d4000, 0x384d6000, +0x384d8000, 0x384da000, 0x384dc000, 0x384de000, 0x384e0000, 0x384e2000, +0x384e4000, 0x384e6000, 0x384e8000, 0x384ea000, 0x384ec000, 0x384ee000, +0x384f0000, 0x384f2000, 0x384f4000, 0x384f6000, 0x384f8000, 0x384fa000, +0x384fc000, 0x384fe000, 0x38500000, 0x38502000, 0x38504000, 0x38506000, +0x38508000, 0x3850a000, 0x3850c000, 0x3850e000, 0x38510000, 0x38512000, +0x38514000, 0x38516000, 0x38518000, 0x3851a000, 0x3851c000, 0x3851e000, +0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852a000, +0x3852c000, 0x3852e000, 0x38530000, 0x38532000, 0x38534000, 0x38536000, +0x38538000, 0x3853a000, 0x3853c000, 0x3853e000, 0x38540000, 0x38542000, +0x38544000, 0x38546000, 0x38548000, 0x3854a000, 0x3854c000, 0x3854e000, +0x38550000, 0x38552000, 0x38554000, 0x38556000, 0x38558000, 0x3855a000, +0x3855c000, 0x3855e000, 0x38560000, 0x38562000, 0x38564000, 0x38566000, +0x38568000, 0x3856a000, 0x3856c000, 0x3856e000, 0x38570000, 0x38572000, +0x38574000, 0x38576000, 0x38578000, 0x3857a000, 0x3857c000, 0x3857e000, +0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858a000, +0x3858c000, 0x3858e000, 0x38590000, 0x38592000, 0x38594000, 0x38596000, +0x38598000, 0x3859a000, 0x3859c000, 0x3859e000, 0x385a0000, 0x385a2000, +0x385a4000, 0x385a6000, 0x385a8000, 0x385aa000, 0x385ac000, 0x385ae000, +0x385b0000, 0x385b2000, 0x385b4000, 0x385b6000, 0x385b8000, 0x385ba000, +0x385bc000, 0x385be000, 0x385c0000, 0x385c2000, 0x385c4000, 0x385c6000, +0x385c8000, 0x385ca000, 0x385cc000, 0x385ce000, 0x385d0000, 0x385d2000, +0x385d4000, 0x385d6000, 0x385d8000, 0x385da000, 0x385dc000, 0x385de000, +0x385e0000, 0x385e2000, 0x385e4000, 0x385e6000, 0x385e8000, 0x385ea000, +0x385ec000, 0x385ee000, 0x385f0000, 0x385f2000, 0x385f4000, 0x385f6000, +0x385f8000, 0x385fa000, 0x385fc000, 0x385fe000, 0x38600000, 0x38602000, +0x38604000, 0x38606000, 0x38608000, 0x3860a000, 0x3860c000, 0x3860e000, +0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861a000, +0x3861c000, 0x3861e000, 0x38620000, 0x38622000, 0x38624000, 0x38626000, +0x38628000, 0x3862a000, 0x3862c000, 0x3862e000, 0x38630000, 0x38632000, +0x38634000, 0x38636000, 0x38638000, 0x3863a000, 0x3863c000, 0x3863e000, +0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864a000, +0x3864c000, 0x3864e000, 0x38650000, 0x38652000, 0x38654000, 0x38656000, +0x38658000, 0x3865a000, 0x3865c000, 0x3865e000, 0x38660000, 0x38662000, +0x38664000, 0x38666000, 0x38668000, 0x3866a000, 0x3866c000, 0x3866e000, +0x38670000, 0x38672000, 0x38674000, 0x38676000, 0x38678000, 0x3867a000, +0x3867c000, 0x3867e000, 0x38680000, 0x38682000, 0x38684000, 0x38686000, +0x38688000, 0x3868a000, 0x3868c000, 0x3868e000, 0x38690000, 0x38692000, +0x38694000, 0x38696000, 0x38698000, 0x3869a000, 0x3869c000, 0x3869e000, +0x386a0000, 0x386a2000, 0x386a4000, 0x386a6000, 0x386a8000, 0x386aa000, +0x386ac000, 0x386ae000, 0x386b0000, 0x386b2000, 0x386b4000, 0x386b6000, +0x386b8000, 0x386ba000, 0x386bc000, 0x386be000, 0x386c0000, 0x386c2000, +0x386c4000, 0x386c6000, 0x386c8000, 0x386ca000, 0x386cc000, 0x386ce000, +0x386d0000, 0x386d2000, 0x386d4000, 0x386d6000, 0x386d8000, 0x386da000, +0x386dc000, 0x386de000, 0x386e0000, 0x386e2000, 0x386e4000, 0x386e6000, +0x386e8000, 0x386ea000, 0x386ec000, 0x386ee000, 0x386f0000, 0x386f2000, +0x386f4000, 0x386f6000, 0x386f8000, 0x386fa000, 0x386fc000, 0x386fe000, +0x38700000, 0x38702000, 0x38704000, 0x38706000, 0x38708000, 0x3870a000, +0x3870c000, 0x3870e000, 0x38710000, 0x38712000, 0x38714000, 0x38716000, +0x38718000, 0x3871a000, 0x3871c000, 0x3871e000, 0x38720000, 0x38722000, +0x38724000, 0x38726000, 0x38728000, 0x3872a000, 0x3872c000, 0x3872e000, +0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873a000, +0x3873c000, 0x3873e000, 0x38740000, 0x38742000, 0x38744000, 0x38746000, +0x38748000, 0x3874a000, 0x3874c000, 0x3874e000, 0x38750000, 0x38752000, +0x38754000, 0x38756000, 0x38758000, 0x3875a000, 0x3875c000, 0x3875e000, +0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876a000, +0x3876c000, 0x3876e000, 0x38770000, 0x38772000, 0x38774000, 0x38776000, +0x38778000, 0x3877a000, 0x3877c000, 0x3877e000, 0x38780000, 0x38782000, +0x38784000, 0x38786000, 0x38788000, 0x3878a000, 0x3878c000, 0x3878e000, +0x38790000, 0x38792000, 0x38794000, 0x38796000, 0x38798000, 0x3879a000, +0x3879c000, 0x3879e000, 0x387a0000, 0x387a2000, 0x387a4000, 0x387a6000, +0x387a8000, 0x387aa000, 0x387ac000, 0x387ae000, 0x387b0000, 0x387b2000, +0x387b4000, 0x387b6000, 0x387b8000, 0x387ba000, 0x387bc000, 0x387be000, +0x387c0000, 0x387c2000, 0x387c4000, 0x387c6000, 0x387c8000, 0x387ca000, +0x387cc000, 0x387ce000, 0x387d0000, 0x387d2000, 0x387d4000, 0x387d6000, +0x387d8000, 0x387da000, 0x387dc000, 0x387de000, 0x387e0000, 0x387e2000, +0x387e4000, 0x387e6000, 0x387e8000, 0x387ea000, 0x387ec000, 0x387ee000, +0x387f0000, 0x387f2000, 0x387f4000, 0x387f6000, 0x387f8000, 0x387fa000, +0x387fc000, 0x387fe000 +}; + +static cmsUInt16Number Offset[64] = { +0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0000, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400 +}; + +static const cmsUInt32Number Exponent[64] = { +0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000, +0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000, +0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000, +0x09000000, 0x09800000, 0x0a000000, 0x0a800000, 0x0b000000, 0x0b800000, +0x0c000000, 0x0c800000, 0x0d000000, 0x0d800000, 0x0e000000, 0x0e800000, +0x0f000000, 0x47800000, 0x80000000, 0x80800000, 0x81000000, 0x81800000, +0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000, +0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000, +0x88000000, 0x88800000, 0x89000000, 0x89800000, 0x8a000000, 0x8a800000, +0x8b000000, 0x8b800000, 0x8c000000, 0x8c800000, 0x8d000000, 0x8d800000, +0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000 +}; + +static const cmsUInt16Number Base[512] = { +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, +0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, +0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, +0x4800, 0x4c00, 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, +0x7000, 0x7400, 0x7800, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, +0x8002, 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, +0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, +0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, +0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, 0xf000, 0xf400, 0xf800, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00 +}; + +static const cmsUInt8Number Shift[512] = { +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, +0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, +0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x0d +}; + +cmsFloat32Number CMSEXPORT _cmsHalf2Float(cmsUInt16Number h) +{ + union { + cmsFloat32Number flt; + cmsUInt32Number num; + } out; + + int n = h >> 10; + + out.num = Mantissa[ (h & 0x3ff) + Offset[ n ] ] + Exponent[ n ]; + return out.flt; +} + +cmsUInt16Number CMSEXPORT _cmsFloat2Half(cmsFloat32Number flt) +{ + union { + cmsFloat32Number flt; + cmsUInt32Number num; + } in; + + cmsUInt32Number n, j; + + in.flt = flt; + n = in.num; + j = (n >> 23) & 0x1ff; + + return (cmsUInt16Number) ((cmsUInt32Number) Base[ j ] + (( n & 0x007fffff) >> Shift[ j ])); +} + +#endif diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsintrp.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsintrp.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsintrp.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsintrp.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1345 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// This module incorporates several interpolation routines, for 1 to 8 channels on input and +// up to 65535 channels on output. The user may change those by using the interpolation plug-in + +// Some people may want to compile as C++ with all warnings on, in this case make compiler silent +#ifdef _MSC_VER +# if (_MSC_VER >= 1400) +# pragma warning( disable : 4365 ) +# endif +#endif + +// Interpolation routines by default +static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); + +// This is the default factory +_cmsInterpPluginChunkType _cmsInterpPluginChunk = { NULL }; + +// The interpolation plug-in memory chunk allocator/dup +void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src) +{ + void* from; + + _cmsAssert(ctx != NULL); + + if (src != NULL) { + from = src ->chunks[InterpPlugin]; + } + else { + static _cmsInterpPluginChunkType InterpPluginChunk = { NULL }; + + from = &InterpPluginChunk; + } + + _cmsAssert(from != NULL); + ctx ->chunks[InterpPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsInterpPluginChunkType)); +} + + +// Main plug-in entry +cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data; + _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); + + if (Data == NULL) { + + ptr ->Interpolators = NULL; + return TRUE; + } + + // Set replacement functions + ptr ->Interpolators = Plugin ->InterpolatorsFactory; + return TRUE; +} + + +// Set the interpolation method +cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p) +{ + _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); + + p ->Interpolation.Lerp16 = NULL; + + // Invoke factory, possibly in the Plug-in + if (ptr ->Interpolators != NULL) + p ->Interpolation = ptr->Interpolators(p -> nInputs, p ->nOutputs, p ->dwFlags); + + // If unsupported by the plug-in, go for the LittleCMS default. + // If happens only if an extern plug-in is being used + if (p ->Interpolation.Lerp16 == NULL) + p ->Interpolation = DefaultInterpolatorsFactory(p ->nInputs, p ->nOutputs, p ->dwFlags); + + // Check for valid interpolator (we just check one member of the union) + if (p ->Interpolation.Lerp16 == NULL) { + return FALSE; + } + + return TRUE; +} + + +// This function precalculates as many parameters as possible to speed up the interpolation. +cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, + const cmsUInt32Number nSamples[], + cmsUInt32Number InputChan, cmsUInt32Number OutputChan, + const void *Table, + cmsUInt32Number dwFlags) +{ + cmsInterpParams* p; + cmsUInt32Number i; + + // Check for maximum inputs + if (InputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", InputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + // Creates an empty object + p = (cmsInterpParams*) _cmsMallocZero(ContextID, sizeof(cmsInterpParams)); + if (p == NULL) return NULL; + + // Keep original parameters + p -> dwFlags = dwFlags; + p -> nInputs = InputChan; + p -> nOutputs = OutputChan; + p ->Table = Table; + p ->ContextID = ContextID; + + // Fill samples per input direction and domain (which is number of nodes minus one) + for (i=0; i < InputChan; i++) { + + p -> nSamples[i] = nSamples[i]; + p -> Domain[i] = nSamples[i] - 1; + } + + // Compute factors to apply to each component to index the grid array + p -> opta[0] = p -> nOutputs; + for (i=1; i < InputChan; i++) + p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i]; + + + if (!_cmsSetInterpolationRoutine(ContextID, p)) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported interpolation (%d->%d channels)", InputChan, OutputChan); + _cmsFree(ContextID, p); + return NULL; + } + + // All seems ok + return p; +} + + +// This one is a wrapper on the anterior, but assuming all directions have same number of nodes +cmsInterpParams* CMSEXPORT _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, + cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags) +{ + int i; + cmsUInt32Number Samples[MAX_INPUT_DIMENSIONS]; + + // Fill the auxiliary array + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Samples[i] = nSamples; + + // Call the extended function + return _cmsComputeInterpParamsEx(ContextID, Samples, InputChan, OutputChan, Table, dwFlags); +} + + +// Free all associated memory +void CMSEXPORT _cmsFreeInterpParams(cmsInterpParams* p) +{ + if (p != NULL) _cmsFree(p ->ContextID, p); +} + + +// Inline fixed point interpolation +cmsINLINE CMS_NO_SANITIZE cmsUInt16Number LinearInterp(cmsS15Fixed16Number a, cmsS15Fixed16Number l, cmsS15Fixed16Number h) +{ + cmsUInt32Number dif = (cmsUInt32Number) (h - l) * a + 0x8000; + dif = (dif >> 16) + l; + return (cmsUInt16Number) (dif); +} + + +// Linear interpolation (Fixed-point optimized) +static +void LinLerp1D(CMSREGISTER const cmsUInt16Number Value[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p) +{ + cmsUInt16Number y1, y0; + int cell0, rest; + int val3; + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + + // if last value... + if (Value[0] == 0xffff) { + + Output[0] = LutTable[p -> Domain[0]]; + } + else + { + val3 = p->Domain[0] * Value[0]; + val3 = _cmsToFixedDomain(val3); // To fixed 15.16 + + cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits + rest = FIXED_REST_TO_INT(val3); // Rest is 16 LSB bits + + y0 = LutTable[cell0]; + y1 = LutTable[cell0 + 1]; + + Output[0] = LinearInterp(rest, y0, y1); + } +} + +// To prevent out of bounds indexing +cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v) +{ + return ((v < 1.0e-9f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v); +} + +// Floating-point version of 1D interpolation +static +void LinLerp1Dfloat(const cmsFloat32Number Value[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + cmsFloat32Number y1, y0; + cmsFloat32Number val2, rest; + int cell0, cell1; + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + + val2 = fclamp(Value[0]); + + // if last value... + if (val2 == 1.0) { + Output[0] = LutTable[p -> Domain[0]]; + } + else + { + val2 *= p->Domain[0]; + + cell0 = (int)floor(val2); + cell1 = (int)ceil(val2); + + // Rest is 16 LSB bits + rest = val2 - cell0; + + y0 = LutTable[cell0]; + y1 = LutTable[cell1]; + + Output[0] = y0 + (y1 - y0) * rest; + } +} + + + +// Eval gray LUT having only one input channel +static CMS_NO_SANITIZE +void Eval1Input(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p16) +{ + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, k1, rk, K0, K1; + int v; + cmsUInt32Number OutChan; + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + + v = Input[0] * p16 -> Domain[0]; + fk = _cmsToFixedDomain(v); + + k0 = FIXED_TO_INT(fk); + rk = (cmsUInt16Number) FIXED_REST_TO_INT(fk); + + k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0); + + K0 = p16 -> opta[0] * k0; + K1 = p16 -> opta[0] * k1; + + for (OutChan=0; OutChan < p16->nOutputs; OutChan++) { + + Output[OutChan] = LinearInterp(rk, LutTable[K0+OutChan], LutTable[K1+OutChan]); + } +} + + + +// Eval gray LUT having only one input channel +static +void Eval1InputFloat(const cmsFloat32Number Value[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + cmsFloat32Number y1, y0; + cmsFloat32Number val2, rest; + int cell0, cell1; + cmsUInt32Number OutChan; + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + + val2 = fclamp(Value[0]); + + // if last value... + if (val2 == 1.0) { + + y0 = LutTable[p->Domain[0]]; + + for (OutChan = 0; OutChan < p->nOutputs; OutChan++) { + Output[OutChan] = y0; + } + } + else + { + val2 *= p->Domain[0]; + + cell0 = (int)floor(val2); + cell1 = (int)ceil(val2); + + // Rest is 16 LSB bits + rest = val2 - cell0; + + cell0 *= p->opta[0]; + cell1 *= p->opta[0]; + + for (OutChan = 0; OutChan < p->nOutputs; OutChan++) { + + y0 = LutTable[cell0 + OutChan]; + y1 = LutTable[cell1 + OutChan]; + + Output[OutChan] = y0 + (y1 - y0) * rest; + } + } +} + +// Bilinear interpolation (16 bits) - cmsFloat32Number version +static +void BilinearInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) + +{ +# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) +# define DENS(i,j) (LutTable[(i)+(j)+OutChan]) + + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + cmsFloat32Number px, py; + int x0, y0, + X0, Y0, X1, Y1; + int TotalOut, OutChan; + cmsFloat32Number fx, fy, + d00, d01, d10, d11, + dx0, dx1, + dxy; + + TotalOut = p -> nOutputs; + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + + x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0; + y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0; + + X0 = p -> opta[1] * x0; + X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[1]); + + Y0 = p -> opta[0] * y0; + Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d00 = DENS(X0, Y0); + d01 = DENS(X0, Y1); + d10 = DENS(X1, Y0); + d11 = DENS(X1, Y1); + + dx0 = LERP(fx, d00, d10); + dx1 = LERP(fx, d01, d11); + + dxy = LERP(fy, dx0, dx1); + + Output[OutChan] = dxy; + } + + +# undef LERP +# undef DENS +} + +// Bilinear interpolation (16 bits) - optimized version +static CMS_NO_SANITIZE +void BilinearInterp16(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p) + +{ +#define DENS(i,j) (LutTable[(i)+(j)+OutChan]) +#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) + + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + int OutChan, TotalOut; + cmsS15Fixed16Number fx, fy; + CMSREGISTER int rx, ry; + int x0, y0; + CMSREGISTER int X0, X1, Y0, Y1; + + int d00, d01, d10, d11, + dx0, dx1, + dxy; + + TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + x0 = FIXED_TO_INT(fx); + rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain + + + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + y0 = FIXED_TO_INT(fy); + ry = FIXED_REST_TO_INT(fy); + + + X0 = p -> opta[1] * x0; + X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[1]); + + Y0 = p -> opta[0] * y0; + Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d00 = DENS(X0, Y0); + d01 = DENS(X0, Y1); + d10 = DENS(X1, Y0); + d11 = DENS(X1, Y1); + + dx0 = LERP(rx, d00, d10); + dx1 = LERP(rx, d01, d11); + + dxy = LERP(ry, dx0, dx1); + + Output[OutChan] = (cmsUInt16Number) dxy; + } + + +# undef LERP +# undef DENS +} + + +// Trilinear interpolation (16 bits) - cmsFloat32Number version +static +void TrilinearInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) + +{ +# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) +# define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) + + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + cmsFloat32Number px, py, pz; + int x0, y0, z0, + X0, Y0, Z0, X1, Y1, Z1; + int TotalOut, OutChan; + + cmsFloat32Number fx, fy, fz, + d000, d001, d010, d011, + d100, d101, d110, d111, + dx00, dx01, dx10, dx11, + dxy0, dxy1, dxyz; + + TotalOut = p -> nOutputs; + + // We need some clipping here + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + pz = fclamp(Input[2]) * p->Domain[2]; + + x0 = (int) floor(px); fx = px - (cmsFloat32Number) x0; // We need full floor funcionality here + y0 = (int) floor(py); fy = py - (cmsFloat32Number) y0; + z0 = (int) floor(pz); fz = pz - (cmsFloat32Number) z0; + + X0 = p -> opta[2] * x0; + X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d000 = DENS(X0, Y0, Z0); + d001 = DENS(X0, Y0, Z1); + d010 = DENS(X0, Y1, Z0); + d011 = DENS(X0, Y1, Z1); + + d100 = DENS(X1, Y0, Z0); + d101 = DENS(X1, Y0, Z1); + d110 = DENS(X1, Y1, Z0); + d111 = DENS(X1, Y1, Z1); + + + dx00 = LERP(fx, d000, d100); + dx01 = LERP(fx, d001, d101); + dx10 = LERP(fx, d010, d110); + dx11 = LERP(fx, d011, d111); + + dxy0 = LERP(fy, dx00, dx10); + dxy1 = LERP(fy, dx01, dx11); + + dxyz = LERP(fz, dxy0, dxy1); + + Output[OutChan] = dxyz; + } + + +# undef LERP +# undef DENS +} + +// Trilinear interpolation (16 bits) - optimized version +static CMS_NO_SANITIZE +void TrilinearInterp16(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p) + +{ +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) + + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + int OutChan, TotalOut; + cmsS15Fixed16Number fx, fy, fz; + CMSREGISTER int rx, ry, rz; + int x0, y0, z0; + CMSREGISTER int X0, X1, Y0, Y1, Z0, Z1; + int d000, d001, d010, d011, + d100, d101, d110, d111, + dx00, dx01, dx10, dx11, + dxy0, dxy1, dxyz; + + TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + x0 = FIXED_TO_INT(fx); + rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain + + + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + y0 = FIXED_TO_INT(fy); + ry = FIXED_REST_TO_INT(fy); + + fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); + z0 = FIXED_TO_INT(fz); + rz = FIXED_REST_TO_INT(fz); + + + X0 = p -> opta[2] * x0; + X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d000 = DENS(X0, Y0, Z0); + d001 = DENS(X0, Y0, Z1); + d010 = DENS(X0, Y1, Z0); + d011 = DENS(X0, Y1, Z1); + + d100 = DENS(X1, Y0, Z0); + d101 = DENS(X1, Y0, Z1); + d110 = DENS(X1, Y1, Z0); + d111 = DENS(X1, Y1, Z1); + + + dx00 = LERP(rx, d000, d100); + dx01 = LERP(rx, d001, d101); + dx10 = LERP(rx, d010, d110); + dx11 = LERP(rx, d011, d111); + + dxy0 = LERP(ry, dx00, dx10); + dxy1 = LERP(ry, dx01, dx11); + + dxyz = LERP(rz, dxy0, dxy1); + + Output[OutChan] = (cmsUInt16Number) dxyz; + } + + +# undef LERP +# undef DENS +} + + +// Tetrahedral interpolation, using Sakamoto algorithm. +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static +void TetrahedralInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number px, py, pz; + int x0, y0, z0, + X0, Y0, Z0, X1, Y1, Z1; + cmsFloat32Number rx, ry, rz; + cmsFloat32Number c0, c1=0, c2=0, c3=0; + int OutChan, TotalOut; + + TotalOut = p -> nOutputs; + + // We need some clipping here + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + pz = fclamp(Input[2]) * p->Domain[2]; + + x0 = (int) floor(px); rx = (px - (cmsFloat32Number) x0); // We need full floor functionality here + y0 = (int) floor(py); ry = (py - (cmsFloat32Number) y0); + z0 = (int) floor(pz); rz = (pz - (cmsFloat32Number) z0); + + + X0 = p -> opta[2] * x0; + X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]); + + for (OutChan=0; OutChan < TotalOut; OutChan++) { + + // These are the 6 Tetrahedral + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Output[OutChan] = c0 + c1 * rx + c2 * ry + c3 * rz; + } + +} + +#undef DENS + +static CMS_NO_SANITIZE +void TetrahedralInterp16(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table; + cmsS15Fixed16Number fx, fy, fz; + cmsS15Fixed16Number rx, ry, rz; + int x0, y0, z0; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + cmsUInt32Number X0, X1, Y0, Y1, Z0, Z1; + cmsUInt32Number TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); + + x0 = FIXED_TO_INT(fx); + y0 = FIXED_TO_INT(fy); + z0 = FIXED_TO_INT(fz); + + rx = FIXED_REST_TO_INT(fx); + ry = FIXED_REST_TO_INT(fy); + rz = FIXED_REST_TO_INT(fz); + + X0 = p -> opta[2] * x0; + X1 = (Input[0] == 0xFFFFU ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = (Input[1] == 0xFFFFU ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = (Input[2] == 0xFFFFU ? 0 : p->opta[0]); + + LutTable += X0+Y0+Z0; + + // Output should be computed as x = ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)) + // which expands as: x = (Rest + ((Rest+0x7fff)/0xFFFF) + 0x8000)>>16 + // This can be replaced by: t = Rest+0x8001, x = (t + (t>>16))>>16 + // at the cost of being off by one at 7fff and 17ffe. + + if (rx >= ry) { + if (ry >= rz) { + Y1 += X1; + Z1 += Y1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c3 -= c2; + c2 -= c1; + c1 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else if (rz >= rx) { + X1 += Z1; + Y1 += X1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c2 -= c1; + c1 -= c3; + c3 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else { + Z1 += X1; + Y1 += Z1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c2 -= c3; + c3 -= c1; + c1 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } + } else { + if (rx >= rz) { + X1 += Y1; + Z1 += X1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c3 -= c1; + c1 -= c2; + c2 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else if (ry >= rz) { + Z1 += Y1; + X1 += Z1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c1 -= c3; + c3 -= c2; + c2 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else { + Y1 += Z1; + X1 += Y1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c1 -= c2; + c2 -= c3; + c3 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } + } +} + + +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static CMS_NO_SANITIZE +void Eval4Inputs(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + cmsS15Fixed16Number fx, fy, fz; + cmsS15Fixed16Number rx, ry, rz; + int x0, y0, z0; + cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + cmsUInt32Number i; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + cmsUInt32Number OutChan; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + + + fk = _cmsToFixedDomain((int) Input[0] * p16 -> Domain[0]); + fx = _cmsToFixedDomain((int) Input[1] * p16 -> Domain[1]); + fy = _cmsToFixedDomain((int) Input[2] * p16 -> Domain[2]); + fz = _cmsToFixedDomain((int) Input[3] * p16 -> Domain[3]); + + k0 = FIXED_TO_INT(fk); + x0 = FIXED_TO_INT(fx); + y0 = FIXED_TO_INT(fy); + z0 = FIXED_TO_INT(fz); + + rk = FIXED_REST_TO_INT(fk); + rx = FIXED_REST_TO_INT(fx); + ry = FIXED_REST_TO_INT(fy); + rz = FIXED_REST_TO_INT(fz); + + K0 = p16 -> opta[3] * k0; + K1 = K0 + (Input[0] == 0xFFFFU ? 0 : p16->opta[3]); + + X0 = p16 -> opta[2] * x0; + X1 = X0 + (Input[1] == 0xFFFFU ? 0 : p16->opta[2]); + + Y0 = p16 -> opta[1] * y0; + Y1 = Y0 + (Input[2] == 0xFFFFU ? 0 : p16->opta[1]); + + Z0 = p16 -> opta[0] * z0; + Z1 = Z0 + (Input[3] == 0xFFFFU ? 0 : p16->opta[0]); + + LutTable = (cmsUInt16Number*) p16 -> Table; + LutTable += K0; + + for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz; + + Tmp1[OutChan] = (cmsUInt16Number)(c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))); + } + + + LutTable = (cmsUInt16Number*) p16 -> Table; + LutTable += K1; + + for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz; + + Tmp2[OutChan] = (cmsUInt16Number) (c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))); + } + + + + for (i=0; i < p16 -> nOutputs; i++) { + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } +} +#undef DENS + + +// For more that 3 inputs (i.e., CMYK) +// evaluate two 3-dimensional interpolations and then linearly interpolate between them. +static +void Eval4InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[3] * k0; + K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[3]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 3*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + TetrahedralInterpFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + TetrahedralInterpFloat(Input + 1, Tmp2, &p1); + + for (i=0; i < p -> nOutputs; i++) + { + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + } +} + +#define EVAL_FNS(N,NM) static CMS_NO_SANITIZE \ +void Eval##N##Inputs(CMSREGISTER const cmsUInt16Number Input[], CMSREGISTER cmsUInt16Number Output[], CMSREGISTER const cmsInterpParams* p16)\ +{\ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;\ + cmsS15Fixed16Number fk;\ + cmsS15Fixed16Number k0, rk;\ + int K0, K1;\ + const cmsUInt16Number* T;\ + cmsUInt32Number i;\ + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];\ + cmsInterpParams p1;\ +\ + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]);\ + k0 = FIXED_TO_INT(fk);\ + rk = FIXED_REST_TO_INT(fk);\ +\ + K0 = p16 -> opta[NM] * k0;\ + K1 = p16 -> opta[NM] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));\ +\ + p1 = *p16;\ + memmove(&p1.Domain[0], &p16 ->Domain[1], NM*sizeof(cmsUInt32Number));\ +\ + T = LutTable + K0;\ + p1.Table = T;\ +\ + Eval##NM##Inputs(Input + 1, Tmp1, &p1);\ +\ + T = LutTable + K1;\ + p1.Table = T;\ +\ + Eval##NM##Inputs(Input + 1, Tmp2, &p1);\ +\ + for (i=0; i < p16 -> nOutputs; i++) {\ +\ + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]);\ + }\ +}\ +\ +static void Eval##N##InputsFloat(const cmsFloat32Number Input[], \ + cmsFloat32Number Output[],\ + const cmsInterpParams * p)\ +{\ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;\ + cmsFloat32Number rest;\ + cmsFloat32Number pk;\ + int k0, K0, K1;\ + const cmsFloat32Number* T;\ + cmsUInt32Number i;\ + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];\ + cmsInterpParams p1;\ +\ + pk = fclamp(Input[0]) * p->Domain[0];\ + k0 = _cmsQuickFloor(pk);\ + rest = pk - (cmsFloat32Number) k0;\ +\ + K0 = p -> opta[NM] * k0;\ + K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[NM]);\ +\ + p1 = *p;\ + memmove(&p1.Domain[0], &p ->Domain[1], NM*sizeof(cmsUInt32Number));\ +\ + T = LutTable + K0;\ + p1.Table = T;\ +\ + Eval##NM##InputsFloat(Input + 1, Tmp1, &p1);\ +\ + T = LutTable + K1;\ + p1.Table = T;\ +\ + Eval##NM##InputsFloat(Input + 1, Tmp2, &p1);\ +\ + for (i=0; i < p -> nOutputs; i++) {\ +\ + cmsFloat32Number y0 = Tmp1[i];\ + cmsFloat32Number y1 = Tmp2[i];\ +\ + Output[i] = y0 + (y1 - y0) * rest;\ + }\ +} + + +/** +* Thanks to Carles Llopis for the templating idea +*/ +EVAL_FNS(5, 4) +EVAL_FNS(6, 5) +EVAL_FNS(7, 6) +EVAL_FNS(8, 7) +EVAL_FNS(9, 8) +EVAL_FNS(10, 9) +EVAL_FNS(11, 10) +EVAL_FNS(12, 11) +EVAL_FNS(13, 12) +EVAL_FNS(14, 13) +EVAL_FNS(15, 14) + + +// The default factory +static +cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags) +{ + + cmsInterpFunction Interpolation; + cmsBool IsFloat = (dwFlags & CMS_LERP_FLAGS_FLOAT); + cmsBool IsTrilinear = (dwFlags & CMS_LERP_FLAGS_TRILINEAR); + + memset(&Interpolation, 0, sizeof(Interpolation)); + + // Safety check + if (nInputChannels >= 4 && nOutputChannels >= MAX_STAGE_CHANNELS) + return Interpolation; + + switch (nInputChannels) { + + case 1: // Gray LUT / linear + + if (nOutputChannels == 1) { + + if (IsFloat) + Interpolation.LerpFloat = LinLerp1Dfloat; + else + Interpolation.Lerp16 = LinLerp1D; + + } + else { + + if (IsFloat) + Interpolation.LerpFloat = Eval1InputFloat; + else + Interpolation.Lerp16 = Eval1Input; + } + break; + + case 2: // Duotone + if (IsFloat) + Interpolation.LerpFloat = BilinearInterpFloat; + else + Interpolation.Lerp16 = BilinearInterp16; + break; + + case 3: // RGB et al + + if (IsTrilinear) { + + if (IsFloat) + Interpolation.LerpFloat = TrilinearInterpFloat; + else + Interpolation.Lerp16 = TrilinearInterp16; + } + else { + + if (IsFloat) + Interpolation.LerpFloat = TetrahedralInterpFloat; + else { + + Interpolation.Lerp16 = TetrahedralInterp16; + } + } + break; + + case 4: // CMYK lut + + if (IsFloat) + Interpolation.LerpFloat = Eval4InputsFloat; + else + Interpolation.Lerp16 = Eval4Inputs; + break; + + case 5: // 5 Inks + if (IsFloat) + Interpolation.LerpFloat = Eval5InputsFloat; + else + Interpolation.Lerp16 = Eval5Inputs; + break; + + case 6: // 6 Inks + if (IsFloat) + Interpolation.LerpFloat = Eval6InputsFloat; + else + Interpolation.Lerp16 = Eval6Inputs; + break; + + case 7: // 7 inks + if (IsFloat) + Interpolation.LerpFloat = Eval7InputsFloat; + else + Interpolation.Lerp16 = Eval7Inputs; + break; + + case 8: // 8 inks + if (IsFloat) + Interpolation.LerpFloat = Eval8InputsFloat; + else + Interpolation.Lerp16 = Eval8Inputs; + break; + + case 9: + if (IsFloat) + Interpolation.LerpFloat = Eval9InputsFloat; + else + Interpolation.Lerp16 = Eval9Inputs; + break; + + case 10: + if (IsFloat) + Interpolation.LerpFloat = Eval10InputsFloat; + else + Interpolation.Lerp16 = Eval10Inputs; + break; + + case 11: + if (IsFloat) + Interpolation.LerpFloat = Eval11InputsFloat; + else + Interpolation.Lerp16 = Eval11Inputs; + break; + + case 12: + if (IsFloat) + Interpolation.LerpFloat = Eval12InputsFloat; + else + Interpolation.Lerp16 = Eval12Inputs; + break; + + case 13: + if (IsFloat) + Interpolation.LerpFloat = Eval13InputsFloat; + else + Interpolation.Lerp16 = Eval13Inputs; + break; + + case 14: + if (IsFloat) + Interpolation.LerpFloat = Eval14InputsFloat; + else + Interpolation.Lerp16 = Eval14Inputs; + break; + + case 15: + if (IsFloat) + Interpolation.LerpFloat = Eval15InputsFloat; + else + Interpolation.Lerp16 = Eval15Inputs; + break; + + default: + Interpolation.Lerp16 = NULL; + } + + return Interpolation; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsio0.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsio0.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsio0.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsio0.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1979 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Generic I/O, tag dictionary management, profile struct + +// IOhandlers are abstractions used by littleCMS to read from whatever file, stream, +// memory block or any storage. Each IOhandler provides implementations for read, +// write, seek and tell functions. LittleCMS code deals with IO across those objects. +// In this way, is easier to add support for new storage media. + +// NULL stream, for taking care of used space ------------------------------------- + +// NULL IOhandler basically does nothing but keep track on how many bytes have been +// written. This is handy when creating profiles, where the file size is needed in the +// header. Then, whole profile is serialized across NULL IOhandler and a second pass +// writes the bytes to the pertinent IOhandler. + +typedef struct { + cmsUInt32Number Pointer; // Points to current location +} FILENULL; + +static +cmsUInt32Number NULLRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + cmsUInt32Number len = size * count; + ResData -> Pointer += len; + return count; + + cmsUNUSED_PARAMETER(Buffer); +} + +static +cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + ResData ->Pointer = offset; + return TRUE; +} + +static +cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + return ResData -> Pointer; +} + +static +cmsBool NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Ptr) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + ResData ->Pointer += size; + if (ResData ->Pointer > iohandler->UsedSpace) + iohandler->UsedSpace = ResData ->Pointer; + + return TRUE; + + cmsUNUSED_PARAMETER(Ptr); +} + +static +cmsBool NULLClose(cmsIOHANDLER* iohandler) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + _cmsFree(iohandler ->ContextID, ResData); + _cmsFree(iohandler ->ContextID, iohandler); + return TRUE; +} + +// The NULL IOhandler creator +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID) +{ + struct _cms_io_handler* iohandler = NULL; + FILENULL* fm = NULL; + + iohandler = (struct _cms_io_handler*) _cmsMallocZero(ContextID, sizeof(struct _cms_io_handler)); + if (iohandler == NULL) return NULL; + + fm = (FILENULL*) _cmsMallocZero(ContextID, sizeof(FILENULL)); + if (fm == NULL) goto Error; + + fm ->Pointer = 0; + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + iohandler ->ReportedSize = 0; + iohandler ->PhysicalFile[0] = 0; + + iohandler ->Read = NULLRead; + iohandler ->Seek = NULLSeek; + iohandler ->Close = NULLClose; + iohandler ->Tell = NULLTell; + iohandler ->Write = NULLWrite; + + return iohandler; + +Error: + if (iohandler) _cmsFree(ContextID, iohandler); + return NULL; + +} + + +// Memory-based stream -------------------------------------------------------------- + +// Those functions implements an iohandler which takes a block of memory as storage medium. + +typedef struct { + cmsUInt8Number* Block; // Points to allocated memory + cmsUInt32Number Size; // Size of allocated memory + cmsUInt32Number Pointer; // Points to current location + int FreeBlockOnClose; // As title + +} FILEMEM; + +static +cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + cmsUInt8Number* Ptr; + cmsUInt32Number len = size * count; + + if (ResData -> Pointer + len > ResData -> Size){ + + len = (ResData -> Size - ResData -> Pointer); + cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size); + return 0; + } + + Ptr = ResData -> Block; + Ptr += ResData -> Pointer; + memmove(Buffer, Ptr, len); + ResData -> Pointer += len; + + return count; +} + +// SEEK_CUR is assumed +static +cmsBool MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (offset > ResData ->Size) { + cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK, "Too few data; probably corrupted profile"); + return FALSE; + } + + ResData ->Pointer = offset; + return TRUE; +} + +// Tell for memory +static +cmsUInt32Number MemoryTell(struct _cms_io_handler* iohandler) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (ResData == NULL) return 0; + return ResData -> Pointer; +} + + +// Writes data to memory, also keeps used space for further reference. +static +cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, const void *Ptr) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (ResData == NULL) return FALSE; // Housekeeping + + // Check for available space. Clip. + if (ResData->Pointer + size > ResData->Size) { + size = ResData ->Size - ResData->Pointer; + } + + if (size == 0) return TRUE; // Write zero bytes is ok, but does nothing + + memmove(ResData ->Block + ResData ->Pointer, Ptr, size); + ResData ->Pointer += size; + + if (ResData ->Pointer > iohandler->UsedSpace) + iohandler->UsedSpace = ResData ->Pointer; + + return TRUE; +} + + +static +cmsBool MemoryClose(struct _cms_io_handler* iohandler) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (ResData ->FreeBlockOnClose) { + + if (ResData ->Block) _cmsFree(iohandler ->ContextID, ResData ->Block); + } + + _cmsFree(iohandler ->ContextID, ResData); + _cmsFree(iohandler ->ContextID, iohandler); + + return TRUE; +} + +// Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes +// a copy of the memory block for letting user to free the memory after invoking open profile. In write +// mode ("w"), Buffer points to the begin of memory block to be written. +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode) +{ + cmsIOHANDLER* iohandler = NULL; + FILEMEM* fm = NULL; + + _cmsAssert(AccessMode != NULL); + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + switch (*AccessMode) { + + case 'r': + fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); + if (fm == NULL) goto Error; + + if (Buffer == NULL) { + cmsSignalError(ContextID, cmsERROR_READ, "Couldn't read profile from NULL pointer"); + goto Error; + } + + fm ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, size); + if (fm ->Block == NULL) { + + _cmsFree(ContextID, fm); + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", (long) size); + return NULL; + } + + + memmove(fm->Block, Buffer, size); + fm ->FreeBlockOnClose = TRUE; + fm ->Size = size; + fm ->Pointer = 0; + iohandler -> ReportedSize = size; + break; + + case 'w': + fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); + if (fm == NULL) goto Error; + + fm ->Block = (cmsUInt8Number*) Buffer; + fm ->FreeBlockOnClose = FALSE; + fm ->Size = size; + fm ->Pointer = 0; + iohandler -> ReportedSize = 0; + break; + + default: + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown access mode '%c'", *AccessMode); + return NULL; + } + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + iohandler ->PhysicalFile[0] = 0; + + iohandler ->Read = MemoryRead; + iohandler ->Seek = MemorySeek; + iohandler ->Close = MemoryClose; + iohandler ->Tell = MemoryTell; + iohandler ->Write = MemoryWrite; + + return iohandler; + +Error: + if (fm) _cmsFree(ContextID, fm); + if (iohandler) _cmsFree(ContextID, iohandler); + return NULL; +} + +// File-based stream ------------------------------------------------------- + +// Read count elements of size bytes each. Return number of elements read +static +cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream); + + if (nReaded != count) { + cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size); + return 0; + } + + return nReaded; +} + +// Position file pointer in the file +static +cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) +{ + if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) { + + cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file"); + return FALSE; + } + + return TRUE; +} + +// Returns file pointer position or 0 on error, which is also a valid position. +static +cmsUInt32Number FileTell(cmsIOHANDLER* iohandler) +{ + long t = ftell((FILE*)iohandler ->stream); + if (t == -1L) { + cmsSignalError(iohandler->ContextID, cmsERROR_FILE, "Tell error; probably corrupted file"); + return 0; + } + + return (cmsUInt32Number)t; +} + +// Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error +static +cmsBool FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer) +{ + if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written + + iohandler->UsedSpace += size; + return (fwrite(Buffer, size, 1, (FILE*)iohandler->stream) == 1); +} + +// Closes the file +static +cmsBool FileClose(cmsIOHANDLER* iohandler) +{ + if (fclose((FILE*) iohandler ->stream) != 0) return FALSE; + _cmsFree(iohandler ->ContextID, iohandler); + return TRUE; +} + +// Create a iohandler for disk based files. +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode) +{ + cmsIOHANDLER* iohandler = NULL; + FILE* fm = NULL; + cmsInt32Number fileLen; + + _cmsAssert(FileName != NULL); + _cmsAssert(AccessMode != NULL); + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + switch (*AccessMode) { + + case 'r': + fm = fopen(FileName, "rb"); + if (fm == NULL) { + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName); + return NULL; + } + fileLen = cmsfilelength(fm); + if (fileLen < 0) + { + fclose(fm); + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName); + return NULL; + } + + iohandler -> ReportedSize = (cmsUInt32Number) fileLen; + break; + + case 'w': + fm = fopen(FileName, "wb"); + if (fm == NULL) { + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName); + return NULL; + } + iohandler -> ReportedSize = 0; + break; + + default: + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode); + return NULL; + } + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + + // Keep track of the original file + strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1); + iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0; + + iohandler ->Read = FileRead; + iohandler ->Seek = FileSeek; + iohandler ->Close = FileClose; + iohandler ->Tell = FileTell; + iohandler ->Write = FileWrite; + + return iohandler; +} + +// Create a iohandler for stream based files +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream) +{ + cmsIOHANDLER* iohandler = NULL; + cmsInt32Number fileSize; + + fileSize = cmsfilelength(Stream); + if (fileSize < 0) + { + cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream"); + return NULL; + } + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + iohandler -> ContextID = ContextID; + iohandler -> stream = (void*) Stream; + iohandler -> UsedSpace = 0; + iohandler -> ReportedSize = (cmsUInt32Number) fileSize; + iohandler -> PhysicalFile[0] = 0; + + iohandler ->Read = FileRead; + iohandler ->Seek = FileSeek; + iohandler ->Close = FileClose; + iohandler ->Tell = FileTell; + iohandler ->Write = FileWrite; + + return iohandler; +} + + + +// Close an open IO handler +cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) +{ + return io -> Close(io); +} + +// ------------------------------------------------------------------------------------------------------- + +cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile; + + if (Icc == NULL) return NULL; + return Icc->IOhandler; +} + +// Creates an empty structure holding all required parameters +cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID) +{ + time_t now = time(NULL); + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE)); + if (Icc == NULL) return NULL; + + Icc ->ContextID = ContextID; + + // Set it to empty + Icc -> TagCount = 0; + + // Set default version + Icc ->Version = 0x02100000; + + // Set creation date/time + memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created)); + + // Create a mutex if the user provided proper plugin. NULL otherwise + Icc ->UsrMutex = _cmsCreateMutex(ContextID); + + // Return the handle + return (cmsHPROFILE) Icc; +} + +cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + if (Icc == NULL) return NULL; + return Icc -> ContextID; +} + + +// Return the number of tags +cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + if (Icc == NULL) return -1; + + return (cmsInt32Number) Icc->TagCount; +} + +// Return the tag signature of a given tag number +cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + if (n > Icc->TagCount) return (cmsTagSignature) 0; // Mark as not available + if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check + + return Icc ->TagNames[n]; +} + + +static +int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig) +{ + int i; + + for (i=0; i < (int) Profile -> TagCount; i++) { + + if (sig == Profile -> TagNames[i]) + return i; + } + + return -1; +} + +// Search for a specific tag in tag dictionary. Returns position or -1 if tag not found. +// If followlinks is turned on, then the position of the linked tag is returned +int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks) +{ + int n; + cmsTagSignature LinkedSig; + + do { + + // Search for given tag in ICC profile directory + n = SearchOneTag(Icc, sig); + if (n < 0) + return -1; // Not found + + if (!lFollowLinks) + return n; // Found, don't follow links + + // Is this a linked tag? + LinkedSig = Icc ->TagLinked[n]; + + // Yes, follow link + if (LinkedSig != (cmsTagSignature) 0) { + sig = LinkedSig; + } + + } while (LinkedSig != (cmsTagSignature) 0); + + return n; +} + +// Deletes a tag entry + +static +void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i) +{ + _cmsAssert(Icc != NULL); + _cmsAssert(i >= 0); + + + if (Icc -> TagPtrs[i] != NULL) { + + // Free previous version + if (Icc ->TagSaveAsRaw[i]) { + _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); + } + else { + cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; + + if (TypeHandler != NULL) { + + cmsTagTypeHandler LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameter + LocalTypeHandler.ICCVersion = Icc ->Version; + LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); + Icc ->TagPtrs[i] = NULL; + } + } + + } +} + + +// Creates a new tag entry +static +cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos) +{ + int i; + + // Search for the tag + i = _cmsSearchTag(Icc, sig, FALSE); + if (i >= 0) { + + // Already exists? delete it + _cmsDeleteTagByPos(Icc, i); + *NewPos = i; + } + else { + + // No, make a new one + if (Icc -> TagCount >= MAX_TABLE_TAG) { + cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); + return FALSE; + } + + *NewPos = (int) Icc ->TagCount; + Icc -> TagCount++; + } + + return TRUE; +} + + +// Check existence +cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile; + return _cmsSearchTag(Icc, sig, FALSE) >= 0; +} + +// Enforces that the profile version is per. spec. +// Operates on the big endian bytes from the profile. +// Called before converting to platform endianness. +// Byte 0 is BCD major version, so max 9. +// Byte 1 is 2 BCD digits, one per nibble. +// Reserved bytes 2 & 3 must be 0. +static +cmsUInt32Number _validatedVersion(cmsUInt32Number DWord) +{ + cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord; + cmsUInt8Number temp1; + cmsUInt8Number temp2; + + if (*pByte > 0x09) *pByte = (cmsUInt8Number) 0x09; + temp1 = (cmsUInt8Number) (*(pByte+1) & 0xf0); + temp2 = (cmsUInt8Number) (*(pByte+1) & 0x0f); + if (temp1 > 0x90U) temp1 = 0x90U; + if (temp2 > 0x09U) temp2 = 0x09U; + *(pByte+1) = (cmsUInt8Number)(temp1 | temp2); + *(pByte+2) = (cmsUInt8Number)0; + *(pByte+3) = (cmsUInt8Number)0; + + return DWord; +} + +// Read profile header and validate it +cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) +{ + cmsTagEntry Tag; + cmsICCHeader Header; + cmsUInt32Number i, j; + cmsUInt32Number HeaderSize; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsUInt32Number TagCount; + + + // Read the header + if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) { + return FALSE; + } + + // Validate file as an ICC profile + if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) { + cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature"); + return FALSE; + } + + // Adjust endianness of the used parameters + Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass); + Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace); + Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs); + + Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent); + Icc -> flags = _cmsAdjustEndianess32(Header.flags); + Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer); + Icc -> model = _cmsAdjustEndianess32(Header.model); + Icc -> creator = _cmsAdjustEndianess32(Header.creator); + + _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes); + Icc -> Version = _cmsAdjustEndianess32(_validatedVersion(Header.version)); + + // Get size as reported in header + HeaderSize = _cmsAdjustEndianess32(Header.size); + + // Make sure HeaderSize is lower than profile size + if (HeaderSize >= Icc ->IOhandler ->ReportedSize) + HeaderSize = Icc ->IOhandler ->ReportedSize; + + + // Get creation date/time + _cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created); + + // The profile ID are 32 raw bytes + memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16); + + + // Read tag directory + if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE; + if (TagCount > MAX_TABLE_TAG) { + + cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount); + return FALSE; + } + + + // Read tag directory + Icc -> TagCount = 0; + for (i=0; i < TagCount; i++) { + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE; + if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE; + if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE; + + // Perform some sanity check. Offset + size should fall inside file. + if (Tag.offset + Tag.size > HeaderSize || + Tag.offset + Tag.size < Tag.offset) + continue; + + Icc -> TagNames[Icc ->TagCount] = Tag.sig; + Icc -> TagOffsets[Icc ->TagCount] = Tag.offset; + Icc -> TagSizes[Icc ->TagCount] = Tag.size; + + // Search for links + for (j=0; j < Icc ->TagCount; j++) { + + if ((Icc ->TagOffsets[j] == Tag.offset) && + (Icc ->TagSizes[j] == Tag.size)) { + + Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; + } + + } + + Icc ->TagCount++; + } + + return TRUE; +} + +// Saves profile header +cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace) +{ + cmsICCHeader Header; + cmsUInt32Number i; + cmsTagEntry Tag; + cmsUInt32Number Count; + + Header.size = _cmsAdjustEndianess32(UsedSpace); + Header.cmmId = _cmsAdjustEndianess32(lcmsSignature); + Header.version = _cmsAdjustEndianess32(Icc ->Version); + + Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass); + Header.colorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace); + Header.pcs = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS); + + // NOTE: in v4 Timestamp must be in UTC rather than in local time + _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created); + + Header.magic = _cmsAdjustEndianess32(cmsMagicNumber); + +#ifdef CMS_IS_WINDOWS_ + Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft); +#else + Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh); +#endif + + Header.flags = _cmsAdjustEndianess32(Icc -> flags); + Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer); + Header.model = _cmsAdjustEndianess32(Icc -> model); + + _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes); + + // Rendering intent in the header (for embedded profiles) + Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent); + + // Illuminant is always D50 + Header.illuminant.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->X)); + Header.illuminant.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y)); + Header.illuminant.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z)); + + // Created by LittleCMS (that's me!) + Header.creator = _cmsAdjustEndianess32(lcmsSignature); + + memset(&Header.reserved, 0, sizeof(Header.reserved)); + + // Set profile ID. Endianness is always big endian + memmove(&Header.profileID, &Icc ->ProfileID, 16); + + // Dump the header + if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE; + + // Saves Tag directory + + // Get true count + Count = 0; + for (i=0; i < Icc -> TagCount; i++) { + if (Icc ->TagNames[i] != (cmsTagSignature) 0) + Count++; + } + + // Store number of tags + if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE; + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue; // It is just a placeholder + + Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagNames[i]); + Tag.offset = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagOffsets[i]); + Tag.size = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagSizes[i]); + + if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE; + } + + return TRUE; +} + +// ----------------------------------------------------------------------- Set/Get several struct members + + +cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> RenderingIntent; +} + +void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> RenderingIntent = RenderingIntent; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return (cmsUInt32Number) Icc -> flags; +} + +void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> flags = (cmsUInt32Number) Flags; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->manufacturer; +} + +void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> manufacturer = manufacturer; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->creator; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->model; +} + +void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> model = model; +} + +void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number)); +} + +void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number)); +} + +void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(ProfileID, Icc ->ProfileID.ID8, 16); +} + +void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(&Icc -> ProfileID, ProfileID, 16); +} + +cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(Dest, &Icc ->Created, sizeof(struct tm)); + return TRUE; +} + +cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> PCS; +} + +void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> PCS = pcs; +} + +cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> ColorSpace; +} + +void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> ColorSpace = sig; +} + +cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> DeviceClass; +} + +void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> DeviceClass = sig; +} + +cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> Version; +} + +void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> Version = Version; +} + +// Get an hexadecimal number with same digits as v +static +cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut) +{ + char Buff[100]; + int i, len; + cmsUInt32Number out; + + for (len=0; in > 0 && len < 100; len++) { + + Buff[len] = (char) (in % BaseIn); + in /= BaseIn; + } + + for (i=len-1, out=0; i >= 0; --i) { + out = out * BaseOut + Buff[i]; + } + + return out; +} + +void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + // 4.2 -> 0x4200000 + + Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16; +} + +cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsUInt32Number n = Icc -> Version >> 16; + + return BaseToBase(n, 16, 10) / 100.0; +} +// -------------------------------------------------------------------------------------------------------------- + + +// Create profile from IOhandler +cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = io; + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + +// Create profile from IOhandler +cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = io; + if (write) { + + NewIcc -> IsWrite = TRUE; + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + + +// Create profile from disk file +cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (*sAccess == 'W' || *sAccess == 'w') { + + NewIcc -> IsWrite = TRUE; + + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess) +{ + return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess); +} + + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (*sAccess == 'w') { + + NewIcc -> IsWrite = TRUE; + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; + +} + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess) +{ + return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess); +} + + +// Open from memory block +cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty; + + hEmpty = cmsCreateProfilePlaceholder(ContextID); + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + // Ok, in this case const void* is casted to void* just because open IO handler + // shares read and writing modes. Don't abuse this feature! + NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r"); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (!_cmsReadHeader(NewIcc)) goto Error; + + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize) +{ + return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize); +} + + + +// Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig +static +cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) +{ + cmsUInt8Number* Data; + cmsUInt32Number i; + cmsUInt32Number Begin; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsTagDescriptor* TagDescriptor; + cmsTagTypeSignature TypeBase; + cmsTagTypeSignature Type; + cmsTagTypeHandler* TypeHandler; + cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc); + cmsTagTypeHandler LocalTypeHandler; + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue; + + // Linked tags are not written + if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue; + + Icc -> TagOffsets[i] = Begin = io ->UsedSpace; + + Data = (cmsUInt8Number*) Icc -> TagPtrs[i]; + + if (!Data) { + + // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user. + // In this case a blind copy of the block data is performed + if (FileOrig != NULL && Icc -> TagOffsets[i]) { + + if (FileOrig->IOhandler != NULL) + { + cmsUInt32Number TagSize = FileOrig->TagSizes[i]; + cmsUInt32Number TagOffset = FileOrig->TagOffsets[i]; + void* Mem; + + if (!FileOrig->IOhandler->Seek(FileOrig->IOhandler, TagOffset)) return FALSE; + + Mem = _cmsMalloc(Icc->ContextID, TagSize); + if (Mem == NULL) return FALSE; + + if (FileOrig->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE; + if (!io->Write(io, TagSize, Mem)) return FALSE; + _cmsFree(Icc->ContextID, Mem); + + Icc->TagSizes[i] = (io->UsedSpace - Begin); + + + // Align to 32 bit boundary. + if (!_cmsWriteAlignment(io)) + return FALSE; + } + } + + continue; + } + + + // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done) + if (Icc ->TagSaveAsRaw[i]) { + + if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE; + } + else { + + // Search for support on this tag + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]); + if (TagDescriptor == NULL) continue; // Unsupported, ignore it + + if (TagDescriptor ->DecideType != NULL) { + + Type = TagDescriptor ->DecideType(Version, Data); + } + else { + + Type = TagDescriptor ->SupportedTypes[0]; + } + + TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); + + if (TypeHandler == NULL) { + cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]); + continue; + } + + TypeBase = TypeHandler ->Signature; + if (!_cmsWriteTypeBase(io, TypeBase)) + return FALSE; + + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) TypeBase); + cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String); + return FALSE; + } + } + + + Icc -> TagSizes[i] = (io ->UsedSpace - Begin); + + // Align to 32 bit boundary. + if (! _cmsWriteAlignment(io)) + return FALSE; + } + + + return TRUE; +} + + +// Fill the offset and size fields for all linked tags +static +cmsBool SetLinks( _cmsICCPROFILE* Icc) +{ + cmsUInt32Number i; + + for (i=0; i < Icc -> TagCount; i++) { + + cmsTagSignature lnk = Icc ->TagLinked[i]; + if (lnk != (cmsTagSignature) 0) { + + int j = _cmsSearchTag(Icc, lnk, FALSE); + if (j >= 0) { + + Icc ->TagOffsets[i] = Icc ->TagOffsets[j]; + Icc ->TagSizes[i] = Icc ->TagSizes[j]; + } + + } + } + + return TRUE; +} + +// Low-level save to IOHANDLER. It returns the number of bytes used to +// store the profile, or zero on error. io may be NULL and in this case +// no data is written--only sizes are calculated +cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + _cmsICCPROFILE Keep; + cmsIOHANDLER* PrevIO = NULL; + cmsUInt32Number UsedSpace; + cmsContext ContextID; + + _cmsAssert(hProfile != NULL); + + if (!_cmsLockMutex(Icc->ContextID, Icc->UsrMutex)) return 0; + memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); + + ContextID = cmsGetProfileContextID(hProfile); + PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID); + if (PrevIO == NULL) { + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + return 0; + } + + // Pass #1 does compute offsets + + if (!_cmsWriteHeader(Icc, 0)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; + + UsedSpace = PrevIO ->UsedSpace; + + // Pass #2 does save to iohandler + + if (io != NULL) { + + Icc ->IOhandler = io; + if (!SetLinks(Icc)) goto Error; + if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; + } + + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + if (!cmsCloseIOhandler(PrevIO)) + UsedSpace = 0; // As a error marker + + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + + return UsedSpace; + + +Error: + cmsCloseIOhandler(PrevIO); + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + + return 0; +} + + +// Low-level save to disk. +cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w"); + cmsBool rc; + + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + if (rc == FALSE) { // remove() is C99 per 7.19.4.1 + remove(FileName); // We have to IGNORE return value in this case + } + return rc; +} + +// Same as anterior, but for streams +cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream) +{ + cmsBool rc; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream); + + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + return rc; +} + + +// Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only +cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded) +{ + cmsBool rc; + cmsIOHANDLER* io; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + _cmsAssert(BytesNeeded != NULL); + + // Should we just calculate the needed space? + if (MemPtr == NULL) { + + *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL); + return (*BytesNeeded == 0) ? FALSE : TRUE; + } + + // That is a real write operation + io = cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w"); + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + return rc; +} + + + +// Closes a profile freeing any involved resources +cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsBool rc = TRUE; + cmsUInt32Number i; + + if (!Icc) return FALSE; + + // Was open in write mode? + if (Icc ->IsWrite) { + + Icc ->IsWrite = FALSE; // Assure no further writing + rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile); + } + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc -> TagPtrs[i]) { + + cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; + + if (TypeHandler != NULL) { + cmsTagTypeHandler LocalTypeHandler = *TypeHandler; + + LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameters + LocalTypeHandler.ICCVersion = Icc ->Version; + LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); + } + else + _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); + } + } + + if (Icc ->IOhandler != NULL) { + rc &= cmsCloseIOhandler(Icc->IOhandler); + } + + _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex); + + _cmsFree(Icc ->ContextID, Icc); // Free placeholder memory + + return rc; +} + + +// ------------------------------------------------------------------------------------------------------------------- + + +// Returns TRUE if a given tag is supported by a plug-in +static +cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type) +{ + cmsUInt32Number i, nMaxTypes; + + nMaxTypes = TagDescriptor->nSupportedTypes; + if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN) + nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN; + + for (i=0; i < nMaxTypes; i++) { + if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE; + } + + return FALSE; +} + + +// That's the main read function +void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsIOHANDLER* io; + cmsTagTypeHandler* TypeHandler; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor; + cmsTagTypeSignature BaseType; + cmsUInt32Number Offset, TagSize; + cmsUInt32Number ElemCount; + int n; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL; + + n = _cmsSearchTag(Icc, sig, TRUE); + if (n < 0) goto Error; // Not found, return NULL + + + // If the element is already in memory, return the pointer + if (Icc -> TagPtrs[n]) { + + if (Icc->TagTypeHandlers[n] == NULL) goto Error; + + // Sanity check + BaseType = Icc->TagTypeHandlers[n]->Signature; + if (BaseType == 0) goto Error; + + TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig); + if (TagDescriptor == NULL) goto Error; + + if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; + + if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc -> TagPtrs[n]; + } + + // We need to read it. Get the offset and size to the file + Offset = Icc -> TagOffsets[n]; + TagSize = Icc -> TagSizes[n]; + + if (TagSize < 8) goto Error; + + io = Icc ->IOhandler; + // Seek to its location + if (!io -> Seek(io, Offset)) + goto Error; + + // Search for support on this tag + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, sig); + + // An unknown element was found. + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String); + goto Error; // Unsupported. + } + + // if supported, get type and check if in list + BaseType = _cmsReadTypeBase(io); + if (BaseType == 0) goto Error; + + if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; + + TagSize -= 8; // Already read by the type base logic + + // Get type handler + TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType); + if (TypeHandler == NULL) goto Error; + LocalTypeHandler = *TypeHandler; + + + // Read the tag + Icc -> TagTypeHandlers[n] = TypeHandler; + + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize); + + // The tag type is supported, but something wrong happened and we cannot read the tag. + // let know the user about this (although it is just a warning) + if (Icc -> TagPtrs[n] == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); + goto Error; + } + + // This is a weird error that may be a symptom of something more serious, the number of + // stored item is actually less than the number of required elements. + if (ElemCount < TagDescriptor ->ElemCount) { + + char String[5]; + + _cmsTagSignature2String(String, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d", + String, TagDescriptor ->ElemCount, ElemCount); + goto Error; + } + + + // Return the data + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc -> TagPtrs[n]; + + + // Return error and unlock tha data +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return NULL; +} + + +// Get true type of data +cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsTagTypeHandler* TypeHandler; + int n; + + // Search for given tag in ICC profile directory + n = _cmsSearchTag(Icc, sig, TRUE); + if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL + + // Get the handler. The true type is there + TypeHandler = Icc -> TagTypeHandlers[n]; + return TypeHandler ->Signature; +} + + +// Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already +// in that list, the previous version is deleted. +cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsTagTypeHandler* TypeHandler = NULL; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor = NULL; + cmsTagTypeSignature Type; + int i; + cmsFloat64Number Version; + char TypeString[5], SigString[5]; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; + + // To delete tags. + if (data == NULL) { + + // Delete the tag + i = _cmsSearchTag(Icc, sig, FALSE); + if (i >= 0) { + + // Use zero as a mark of deleted + _cmsDeleteTagByPos(Icc, i); + Icc ->TagNames[i] = (cmsTagSignature) 0; + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; + } + // Didn't find the tag + goto Error; + } + + if (!_cmsNewTag(Icc, sig, &i)) goto Error; + + // This is not raw + Icc ->TagSaveAsRaw[i] = FALSE; + + // This is not a link + Icc ->TagLinked[i] = (cmsTagSignature) 0; + + // Get information about the TAG. + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL){ + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); + goto Error; + } + + + // Now we need to know which type to use. It depends on the version. + Version = cmsGetProfileVersion(hProfile); + + if (TagDescriptor ->DecideType != NULL) { + + // Let the tag descriptor to decide the type base on depending on + // the data. This is useful for example on parametric curves, where + // curves specified by a table cannot be saved as parametric and needs + // to be casted to single v2-curves, even on v4 profiles. + + Type = TagDescriptor ->DecideType(Version, data); + } + else { + + Type = TagDescriptor ->SupportedTypes[0]; + } + + // Does the tag support this type? + if (!IsTypeSupported(TagDescriptor, Type)) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); + goto Error; + } + + // Does we have a handler for this type? + TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); + if (TypeHandler == NULL) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); + goto Error; // Should never happen + } + + + // Fill fields on icc structure + Icc ->TagTypeHandlers[i] = TypeHandler; + Icc ->TagNames[i] = sig; + Icc ->TagSizes[i] = 0; + Icc ->TagOffsets[i] = 0; + + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); + + if (Icc ->TagPtrs[i] == NULL) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString); + + goto Error; + } + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; + +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + +} + +// Read and write raw data. The only way those function would work and keep consistence with normal read and write +// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained +// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where +// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows +// to write a tag as raw data and the read it as handled. + +cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + void *Object; + int i; + cmsIOHANDLER* MemIO; + cmsTagTypeHandler* TypeHandler = NULL; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor = NULL; + cmsUInt32Number rc; + cmsUInt32Number Offset, TagSize; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + // Search for given tag in ICC profile directory + i = _cmsSearchTag(Icc, sig, TRUE); + if (i < 0) goto Error; // Not found, + + // It is already read? + if (Icc -> TagPtrs[i] == NULL) { + + // No yet, get original position + Offset = Icc ->TagOffsets[i]; + TagSize = Icc ->TagSizes[i]; + + // read the data directly, don't keep copy + if (data != NULL) { + + if (BufferSize < TagSize) + TagSize = BufferSize; + + if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error; + if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TagSize; + } + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc ->TagSizes[i]; + } + + // The data has been already read, or written. But wait!, maybe the user chose to save as + // raw data. In this case, return the raw data directly + if (Icc ->TagSaveAsRaw[i]) { + + if (data != NULL) { + + TagSize = Icc ->TagSizes[i]; + if (BufferSize < TagSize) + TagSize = BufferSize; + + memmove(data, Icc ->TagPtrs[i], TagSize); + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TagSize; + } + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc ->TagSizes[i]; + } + + // Already read, or previously set by cmsWriteTag(). We need to serialize that + // data to raw in order to maintain consistency. + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + Object = cmsReadTag(hProfile, sig); + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + if (Object == NULL) goto Error; + + // Now we need to serialize to a memory block: just use a memory iohandler + + if (data == NULL) { + MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile)); + } else{ + MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); + } + if (MemIO == NULL) goto Error; + + // Obtain type handling for the tag + TypeHandler = Icc ->TagTypeHandlers[i]; + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + if (TypeHandler == NULL) goto Error; + + // Serialize + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + + if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + // Get Size and close + rc = MemIO ->Tell(MemIO); + cmsCloseIOhandler(MemIO); // Ignore return code this time + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return rc; + +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return 0; +} + +// Similar to the anterior. This function allows to write directly to the ICC profile any data, without +// checking anything. As a rule, mixing Raw with cooked doesn't work, so writing a tag as raw and then reading +// it as cooked without serializing does result into an error. If that is what you want, you will need to dump +// the profile to memry or disk and then reopen it. +cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + if (!_cmsNewTag(Icc, sig, &i)) { + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + } + + // Mark the tag as being written as RAW + Icc ->TagSaveAsRaw[i] = TRUE; + Icc ->TagNames[i] = sig; + Icc ->TagLinked[i] = (cmsTagSignature) 0; + + // Keep a copy of the block + Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); + Icc ->TagSizes[i] = Size; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + + if (Icc->TagPtrs[i] == NULL) { + Icc->TagNames[i] = (cmsTagSignature) 0; + return FALSE; + } + return TRUE; +} + +// Using this function you can collapse several tag entries to the same block in the profile +cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; + + if (!_cmsNewTag(Icc, sig, &i)) { + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + } + + // Keep necessary information + Icc ->TagSaveAsRaw[i] = FALSE; + Icc ->TagNames[i] = sig; + Icc ->TagLinked[i] = dest; + + Icc ->TagPtrs[i] = NULL; + Icc ->TagSizes[i] = 0; + Icc ->TagOffsets[i] = 0; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; +} + + +// Returns the tag linked to sig, in the case two tags are sharing same resource +cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + // Search for given tag in ICC profile directory + i = _cmsSearchTag(Icc, sig, FALSE); + if (i < 0) return (cmsTagSignature) 0; // Not found, return 0 + + return Icc -> TagLinked[i]; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsio1.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsio1.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsio1.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsio1.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1057 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Read tags using low-level functions, provides necessary glue code to adapt versions, etc. + +// LUT tags +static const cmsTagSignature Device2PCS16[] = {cmsSigAToB0Tag, // Perceptual + cmsSigAToB1Tag, // Relative colorimetric + cmsSigAToB2Tag, // Saturation + cmsSigAToB1Tag }; // Absolute colorimetric + +static const cmsTagSignature Device2PCSFloat[] = {cmsSigDToB0Tag, // Perceptual + cmsSigDToB1Tag, // Relative colorimetric + cmsSigDToB2Tag, // Saturation + cmsSigDToB3Tag }; // Absolute colorimetric + +static const cmsTagSignature PCS2Device16[] = {cmsSigBToA0Tag, // Perceptual + cmsSigBToA1Tag, // Relative colorimetric + cmsSigBToA2Tag, // Saturation + cmsSigBToA1Tag }; // Absolute colorimetric + +static const cmsTagSignature PCS2DeviceFloat[] = {cmsSigBToD0Tag, // Perceptual + cmsSigBToD1Tag, // Relative colorimetric + cmsSigBToD2Tag, // Saturation + cmsSigBToD3Tag }; // Absolute colorimetric + + +// Factors to convert from 1.15 fixed point to 0..1.0 range and vice-versa +#define InpAdj (1.0/MAX_ENCODEABLE_XYZ) // (65536.0/(65535.0*2.0)) +#define OutpAdj (MAX_ENCODEABLE_XYZ) // ((2.0*65535.0)/65536.0) + +// Several resources for gray conversions. +static const cmsFloat64Number GrayInputMatrix[] = { (InpAdj*cmsD50X), (InpAdj*cmsD50Y), (InpAdj*cmsD50Z) }; +static const cmsFloat64Number OneToThreeInputMatrix[] = { 1, 1, 1 }; +static const cmsFloat64Number PickYMatrix[] = { 0, (OutpAdj*cmsD50Y), 0 }; +static const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 }; + +// Get a media white point fixing some issues found in certain old profiles +cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile) +{ + cmsCIEXYZ* Tag; + + _cmsAssert(Dest != NULL); + + Tag = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); + + // If no wp, take D50 + if (Tag == NULL) { + *Dest = *cmsD50_XYZ(); + return TRUE; + } + + // V2 display profiles should give D50 + if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { + + if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { + *Dest = *cmsD50_XYZ(); + return TRUE; + } + } + + // All seems ok + *Dest = *Tag; + return TRUE; +} + + +// Chromatic adaptation matrix. Fix some issues as well +cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile) +{ + cmsMAT3* Tag; + + _cmsAssert(Dest != NULL); + + Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag); + + if (Tag != NULL) { + *Dest = *Tag; + return TRUE; + } + + // No CHAD available, default it to identity + _cmsMAT3identity(Dest); + + // V2 display profiles should give D50 + if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { + + if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { + + cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); + + if (White == NULL) { + + _cmsMAT3identity(Dest); + return TRUE; + } + + return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ()); + } + } + + return TRUE; +} + + +// Auxiliary, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper +static +cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile) +{ + cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue; + + _cmsAssert(r != NULL); + + PtrRed = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag); + PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag); + PtrBlue = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag); + + if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL) + return FALSE; + + _cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X, PtrBlue -> X); + _cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y, PtrBlue -> Y); + _cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z, PtrBlue -> Z); + + return TRUE; +} + + +// Gray input pipeline +static +cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile) +{ + cmsToneCurve *GrayTRC; + cmsPipeline* Lut; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); + if (GrayTRC == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 1, 3); + if (Lut == NULL) + goto Error; + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + // In this case we implement the profile as an identity matrix plus 3 tone curves + cmsUInt16Number Zero[2] = { 0x8080, 0x8080 }; + cmsToneCurve* EmptyTab; + cmsToneCurve* LabCurves[3]; + + EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); + + if (EmptyTab == NULL) + goto Error; + + LabCurves[0] = GrayTRC; + LabCurves[1] = EmptyTab; + LabCurves[2] = EmptyTab; + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) { + cmsFreeToneCurve(EmptyTab); + goto Error; + } + + cmsFreeToneCurve(EmptyTab); + + } + else { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL))) + goto Error; + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + +// RGB Matrix shaper +static +cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile) +{ + cmsPipeline* Lut; + cmsMAT3 Mat; + cmsToneCurve *Shapes[3]; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + int i, j; + + if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL; + + // XYZ PCS in encoded in 1.15 format, and the matrix output comes in 0..0xffff range, so + // we need to adjust the output by a factor of (0x10000/0xffff) to put data in + // a 1.16 range, and then a >> 1 to obtain 1.15. The total factor is (65536.0)/(65535.0*2) + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + Mat.v[i].n[j] *= InpAdj; + + + Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); + Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); + Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); + + if (!Shapes[0] || !Shapes[1] || !Shapes[2]) + return NULL; + + Lut = cmsPipelineAlloc(ContextID, 3, 3); + if (Lut != NULL) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL))) + goto Error; + + // Note that it is certainly possible a single profile would have a LUT based + // tag for output working in lab and a matrix-shaper for the fallback cases. + // This is not allowed by the spec, but this code is tolerant to those cases + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocXYZ2Lab(ContextID))) + goto Error; + } + + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + + + +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + + if (Lut == NULL) return NULL; + + // input and output of transform are in lcms 0..1 encoding. If XYZ or Lab spaces are used, + // these need to be normalized into the appropriate ranges (Lab = 100,0,0, XYZ=1.0,1.0,1.0) + if ( spc == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else if (spc == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + if ( PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else if( PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + + +// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc +// is adjusted here in order to create a LUT that takes care of all those details. +// We add intent = 0xffffffff as a way to read matrix shaper always, no matter of other LUT +cmsPipeline* CMSEXPORT _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16; + cmsTagSignature tagFloat; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + // On named color, take the appropriate tag + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + cmsPipeline* Lut; + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag); + + if (nc == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 0, 0); + if (Lut == NULL) { + cmsFreeNamedColorList(nc); + return NULL; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) { + cmsPipelineFree(Lut); + return NULL; + } + return Lut; + } + + // This is an attempt to reuse this function to retrieve the matrix-shaper as pipeline no + // matter other LUT are present and have precedence. Intent = 0xffffffff can be used for that. + if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) { + + tag16 = Device2PCS16[Intent]; + tagFloat = Device2PCSFloat[Intent]; + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V4, but the encoding range is no + // longer 0..1.0, so we need to add an stage depending on the color space + return _cmsReadFloatInputTag(hProfile, tagFloat); + } + + // Revert to perceptual if no tag is found + if (!cmsIsTag(hProfile, tag16)) { + tag16 = Device2PCS16[0]; + } + + if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // First read the tag + cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // After reading it, we have now info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + + // We need to adjust data only for Lab16 on output + if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) + return Lut; + + // If the input is Lab, add also a conversion at the begin + if (cmsGetColorSpace(hProfile) == cmsSigLabData && + !cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + + // Add a matrix for conversion V2 to V4 Lab PCS + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; + } + } + + // Lut was not found, try to create a matrix-shaper + + // Check if this is a grayscale profile. + if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { + + // if so, build appropriate conversion tables. + // The tables are the PCS iluminant, scaled across GrayTRC + return BuildGrayInputMatrixPipeline(hProfile); + } + + // Not gray, create a normal matrix-shaper + return BuildRGBInputMatrixShaper(hProfile); +} + +// --------------------------------------------------------------------------------------------------------------- + +// Gray output pipeline. +// XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be +// given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve. +// The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well. + +static +cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile) +{ + cmsToneCurve *GrayTRC, *RevGrayTRC; + cmsPipeline* Lut; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); + if (GrayTRC == NULL) return NULL; + + RevGrayTRC = cmsReverseToneCurve(GrayTRC); + if (RevGrayTRC == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 3, 1); + if (Lut == NULL) { + cmsFreeToneCurve(RevGrayTRC); + return NULL; + } + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL))) + goto Error; + } + else { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL))) + goto Error; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC))) + goto Error; + + cmsFreeToneCurve(RevGrayTRC); + return Lut; + +Error: + cmsFreeToneCurve(RevGrayTRC); + cmsPipelineFree(Lut); + return NULL; +} + + +static +cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile) +{ + cmsPipeline* Lut; + cmsToneCurve *Shapes[3], *InvShapes[3]; + cmsMAT3 Mat, Inv; + int i, j; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) + return NULL; + + if (!_cmsMAT3inverse(&Mat, &Inv)) + return NULL; + + // XYZ PCS in encoded in 1.15 format, and the matrix input should come in 0..0xffff range, so + // we need to adjust the input by a << 1 to obtain a 1.16 fixed and then by a factor of + // (0xffff/0x10000) to put data in 0..0xffff range. Total factor is (2.0*65535.0)/65536.0; + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + Inv.v[i].n[j] *= OutpAdj; + + Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); + Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); + Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); + + if (!Shapes[0] || !Shapes[1] || !Shapes[2]) + return NULL; + + InvShapes[0] = cmsReverseToneCurve(Shapes[0]); + InvShapes[1] = cmsReverseToneCurve(Shapes[1]); + InvShapes[2] = cmsReverseToneCurve(Shapes[2]); + + if (!InvShapes[0] || !InvShapes[1] || !InvShapes[2]) { + return NULL; + } + + Lut = cmsPipelineAlloc(ContextID, 3, 3); + if (Lut != NULL) { + + // Note that it is certainly possible a single profile would have a LUT based + // tag for output working in lab and a matrix-shaper for the fallback cases. + // This is not allowed by the spec, but this code is tolerant to those cases + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLab2XYZ(ContextID))) + goto Error; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes))) + goto Error; + } + + cmsFreeToneCurveTriple(InvShapes); + return Lut; +Error: + cmsFreeToneCurveTriple(InvShapes); + cmsPipelineFree(Lut); + return NULL; +} + + +// Change CLUT interpolation to trilinear +static +void ChangeInterpolationToTrilinear(cmsPipeline* Lut) +{ + cmsStage* Stage; + + for (Stage = cmsPipelineGetPtrToFirstStage(Lut); + Stage != NULL; + Stage = cmsStageNext(Stage)) { + + if (cmsStageType(Stage) == cmsSigCLutElemType) { + + _cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data; + + CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR; + _cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params); + } + } +} + + +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile); + + if (Lut == NULL) return NULL; + + // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding, + // and since the formatter has already accommodated to 0..1.0, we should undo this change + if ( PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else + if (PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline + if ( dataSpace == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else if (dataSpace == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + +// Create an output MPE LUT from agiven profile. Version mismatches are handled here +cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16; + cmsTagSignature tagFloat; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + + if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) { + + tag16 = PCS2Device16[Intent]; + tagFloat = PCS2DeviceFloat[Intent]; + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V4 + return _cmsReadFloatOutputTag(hProfile, tagFloat); + } + + // Revert to perceptual if no tag is found + if (!cmsIsTag(hProfile, tag16)) { + tag16 = PCS2Device16[0]; + } + + if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // First read the tag + cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // After reading it, we have info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + if (Lut == NULL) return NULL; + + // Now it is time for a controversial stuff. I found that for 3D LUTS using + // Lab used as indexer space, trilinear interpolation should be used + if (cmsGetPCS(hProfile) == cmsSigLabData) + ChangeInterpolationToTrilinear(Lut); + + // We need to adjust data only for Lab and Lut16 type + if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) + return Lut; + + // Add a matrix for conversion V4 to V2 Lab PCS + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + + // If the output is Lab, add also a conversion at the end + if (cmsGetColorSpace(hProfile) == cmsSigLabData) + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; + } + } + + // Lut not found, try to create a matrix-shaper + + // Check if this is a grayscale profile. + if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { + + // if so, build appropriate conversion tables. + // The tables are the PCS iluminant, scaled across GrayTRC + return BuildGrayOutputPipeline(hProfile); + } + + // Not gray, create a normal matrix-shaper, which only operates in XYZ space + return BuildRGBOutputMatrixShaper(hProfile); +} + +// --------------------------------------------------------------------------------------------------------------- + +// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); + + if (Lut == NULL) return NULL; + + if (spc == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else + if (spc == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + if (PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else + if (PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; +} + +// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The +// tag name here may default to AToB0 +cmsPipeline* CMSEXPORT _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsPipeline* Lut; + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16; + cmsTagSignature tagFloat; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + + if (Intent > INTENT_ABSOLUTE_COLORIMETRIC) + return NULL; + + tag16 = Device2PCS16[Intent]; + tagFloat = Device2PCSFloat[Intent]; + + // On named color, take the appropriate tag + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*)cmsReadTag(hProfile, cmsSigNamedColor2Tag); + + if (nc == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 0, 0); + if (Lut == NULL) + goto Error; + + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE))) + goto Error; + + if (cmsGetColorSpace(hProfile) == cmsSigLabData) + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; + Error: + cmsPipelineFree(Lut); + cmsFreeNamedColorList(nc); + return NULL; + } + + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V + return _cmsReadFloatDevicelinkTag(hProfile, tagFloat); + } + + tagFloat = Device2PCSFloat[0]; + if (cmsIsTag(hProfile, tagFloat)) { + + return cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat)); + } + + if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + tag16 = Device2PCS16[0]; + if (!cmsIsTag(hProfile, tag16)) return NULL; + } + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // Read the tag + Lut = (cmsPipeline*)cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + if (Lut == NULL) return NULL; + + // Now it is time for a controversial stuff. I found that for 3D LUTS using + // Lab used as indexer space, trilinear interpolation should be used + if (cmsGetPCS(hProfile) == cmsSigLabData) + ChangeInterpolationToTrilinear(Lut); + + // After reading it, we have info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // We need to adjust data for Lab16 on output + if (OriginalType != cmsSigLut16Type) return Lut; + + // Here it is possible to get Lab on both sides + + if (cmsGetColorSpace(hProfile) == cmsSigLabData) { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error2; + } + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error2; + } + + return Lut; + +Error2: + cmsPipelineFree(Lut); + return NULL; +} + +// --------------------------------------------------------------------------------------------------------------- + +// Returns TRUE if the profile is implemented as matrix-shaper +cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile) +{ + switch (cmsGetColorSpace(hProfile)) { + + case cmsSigGrayData: + + return cmsIsTag(hProfile, cmsSigGrayTRCTag); + + case cmsSigRgbData: + + return (cmsIsTag(hProfile, cmsSigRedColorantTag) && + cmsIsTag(hProfile, cmsSigGreenColorantTag) && + cmsIsTag(hProfile, cmsSigBlueColorantTag) && + cmsIsTag(hProfile, cmsSigRedTRCTag) && + cmsIsTag(hProfile, cmsSigGreenTRCTag) && + cmsIsTag(hProfile, cmsSigBlueTRCTag)); + + default: + + return FALSE; + } +} + +// Returns TRUE if the intent is implemented as CLUT +cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection) +{ + const cmsTagSignature* TagTable; + + // For devicelinks, the supported intent is that one stated in the header + if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) { + return (cmsGetHeaderRenderingIntent(hProfile) == Intent); + } + + switch (UsedDirection) { + + case LCMS_USED_AS_INPUT: TagTable = Device2PCS16; break; + case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device16; break; + + // For proofing, we need rel. colorimetric in output. Let's do some recursion + case LCMS_USED_AS_PROOF: + return cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) && + cmsIsIntentSupported(hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT); + + default: + cmsSignalError(cmsGetProfileContextID(hProfile), cmsERROR_RANGE, "Unexpected direction (%d)", UsedDirection); + return FALSE; + } + + return cmsIsTag(hProfile, TagTable[Intent]); + +} + + +// Return info about supported intents +cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number UsedDirection) +{ + + if (cmsIsCLUT(hProfile, Intent, UsedDirection)) return TRUE; + + // Is there any matrix-shaper? If so, the intent is supported. This is a bit odd, since V2 matrix shaper + // does not fully support relative colorimetric because they cannot deal with non-zero black points, but + // many profiles claims that, and this is certainly not true for V4 profiles. Lets answer "yes" no matter + // the accuracy would be less than optimal in rel.col and v2 case. + + return cmsIsMatrixShaper(hProfile); +} + + +// --------------------------------------------------------------------------------------------------------------- + +// Read both, profile sequence description and profile sequence id if present. Then combine both to +// create qa unique structure holding both. Shame on ICC to store things in such complicated way. +cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile) +{ + cmsSEQ* ProfileSeq; + cmsSEQ* ProfileId; + cmsSEQ* NewSeq; + cmsUInt32Number i; + + // Take profile sequence description first + ProfileSeq = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceDescTag); + + // Take profile sequence ID + ProfileId = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceIdTag); + + if (ProfileSeq == NULL && ProfileId == NULL) return NULL; + + if (ProfileSeq == NULL) return cmsDupProfileSequenceDescription(ProfileId); + if (ProfileId == NULL) return cmsDupProfileSequenceDescription(ProfileSeq); + + // We have to mix both together. For that they must agree + if (ProfileSeq ->n != ProfileId ->n) return cmsDupProfileSequenceDescription(ProfileSeq); + + NewSeq = cmsDupProfileSequenceDescription(ProfileSeq); + + // Ok, proceed to the mixing + if (NewSeq != NULL) { + for (i=0; i < ProfileSeq ->n; i++) { + + memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID)); + NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description); + } + } + return NewSeq; +} + +// Dump the contents of profile sequence in both tags (if v4 available) +cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq) +{ + if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE; + + if (cmsGetEncodedICCversion(hProfile) >= 0x4000000) { + + if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE; + } + + return TRUE; +} + + +// Auxiliary, read and duplicate a MLU if found. +static +cmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig) +{ + cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig); + if (mlu == NULL) return NULL; + + return cmsMLUdup(mlu); +} + +// Create a sequence description out of an array of profiles +cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]) +{ + cmsUInt32Number i; + cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles); + + if (seq == NULL) return NULL; + + for (i=0; i < nProfiles; i++) { + + cmsPSEQDESC* ps = &seq ->seq[i]; + cmsHPROFILE h = hProfiles[i]; + cmsTechnologySignature* techpt; + + cmsGetHeaderAttributes(h, &ps ->attributes); + cmsGetHeaderProfileID(h, ps ->ProfileID.ID8); + ps ->deviceMfg = cmsGetHeaderManufacturer(h); + ps ->deviceModel = cmsGetHeaderModel(h); + + techpt = (cmsTechnologySignature*) cmsReadTag(h, cmsSigTechnologyTag); + if (techpt == NULL) + ps ->technology = (cmsTechnologySignature) 0; + else + ps ->technology = *techpt; + + ps ->Manufacturer = GetMLUFromProfile(h, cmsSigDeviceMfgDescTag); + ps ->Model = GetMLUFromProfile(h, cmsSigDeviceModelDescTag); + ps ->Description = GetMLUFromProfile(h, cmsSigProfileDescriptionTag); + + } + + return seq; +} + +// ------------------------------------------------------------------------------------------------------------------- + + +static +const cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info) +{ + cmsTagSignature sig; + + switch (Info) { + + case cmsInfoDescription: + sig = cmsSigProfileDescriptionTag; + break; + + case cmsInfoManufacturer: + sig = cmsSigDeviceMfgDescTag; + break; + + case cmsInfoModel: + sig = cmsSigDeviceModelDescTag; + break; + + case cmsInfoCopyright: + sig = cmsSigCopyrightTag; + break; + + default: return NULL; + } + + + return (cmsMLU*) cmsReadTag(hProfile, sig); +} + + + +cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize) +{ + const cmsMLU* mlu = GetInfo(hProfile, Info); + if (mlu == NULL) return 0; + + return cmsMLUgetWide(mlu, LanguageCode, CountryCode, Buffer, BufferSize); +} + + +cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize) +{ + const cmsMLU* mlu = GetInfo(hProfile, Info); + if (mlu == NULL) return 0; + + return cmsMLUgetASCII(mlu, LanguageCode, CountryCode, Buffer, BufferSize); +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmslut.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmslut.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmslut.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmslut.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1866 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// Allocates an empty multi profile element +cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, + cmsStageSignature Type, + cmsUInt32Number InputChannels, + cmsUInt32Number OutputChannels, + _cmsStageEvalFn EvalPtr, + _cmsStageDupElemFn DupElemPtr, + _cmsStageFreeElemFn FreePtr, + void* Data) +{ + cmsStage* ph = (cmsStage*) _cmsMallocZero(ContextID, sizeof(cmsStage)); + + if (ph == NULL) return NULL; + + + ph ->ContextID = ContextID; + + ph ->Type = Type; + ph ->Implements = Type; // By default, no clue on what is implementing + + ph ->InputChannels = InputChannels; + ph ->OutputChannels = OutputChannels; + ph ->EvalPtr = EvalPtr; + ph ->DupElemPtr = DupElemPtr; + ph ->FreePtr = FreePtr; + ph ->Data = Data; + + return ph; +} + + +static +void EvaluateIdentity(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + memmove(Out, In, mpe ->InputChannels * sizeof(cmsFloat32Number)); +} + + +cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels) +{ + return _cmsStageAllocPlaceholder(ContextID, + cmsSigIdentityElemType, + nChannels, nChannels, + EvaluateIdentity, + NULL, + NULL, + NULL); + } + +// Conversion functions. From floating point to 16 bits +static +void FromFloatTo16(const cmsFloat32Number In[], cmsUInt16Number Out[], cmsUInt32Number n) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + Out[i] = _cmsQuickSaturateWord(In[i] * 65535.0); + } +} + +// From 16 bits to floating point +static +void From16ToFloat(const cmsUInt16Number In[], cmsFloat32Number Out[], cmsUInt32Number n) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + Out[i] = (cmsFloat32Number) In[i] / 65535.0F; + } +} + + +// This function is quite useful to analyze the structure of a LUT and retrieve the MPE elements +// that conform the LUT. It should be called with the LUT, the number of expected elements and +// then a list of expected types followed with a list of cmsFloat64Number pointers to MPE elements. If +// the function founds a match with current pipeline, it fills the pointers and returns TRUE +// if not, returns FALSE without touching anything. Setting pointers to NULL does bypass +// the storage process. +cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...) +{ + va_list args; + cmsUInt32Number i; + cmsStage* mpe; + cmsStageSignature Type; + void** ElemPtr; + + // Make sure same number of elements + if (cmsPipelineStageCount(Lut) != n) return FALSE; + + va_start(args, n); + + // Iterate across asked types + mpe = Lut ->Elements; + for (i=0; i < n; i++) { + + // Get asked type. cmsStageSignature is promoted to int by compiler + Type = (cmsStageSignature)va_arg(args, int); + if (mpe ->Type != Type) { + + va_end(args); // Mismatch. We are done. + return FALSE; + } + mpe = mpe ->Next; + } + + // Found a combination, fill pointers if not NULL + mpe = Lut ->Elements; + for (i=0; i < n; i++) { + + ElemPtr = va_arg(args, void**); + if (ElemPtr != NULL) + *ElemPtr = mpe; + + mpe = mpe ->Next; + } + + va_end(args); + return TRUE; +} + +// Below there are implementations for several types of elements. Each type may be implemented by a +// evaluation function, a duplication function, a function to free resources and a constructor. + +// ************************************************************************************************* +// Type cmsSigCurveSetElemType (curves) +// ************************************************************************************************* + +cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; + + return Data ->TheCurves; +} + +static +void EvaluateCurves(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + _cmsStageToneCurvesData* Data; + cmsUInt32Number i; + + _cmsAssert(mpe != NULL); + + Data = (_cmsStageToneCurvesData*) mpe ->Data; + if (Data == NULL) return; + + if (Data ->TheCurves == NULL) return; + + for (i=0; i < Data ->nCurves; i++) { + Out[i] = cmsEvalToneCurveFloat(Data ->TheCurves[i], In[i]); + } +} + +static +void CurveSetElemTypeFree(cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data; + cmsUInt32Number i; + + _cmsAssert(mpe != NULL); + + Data = (_cmsStageToneCurvesData*) mpe ->Data; + if (Data == NULL) return; + + if (Data ->TheCurves != NULL) { + for (i=0; i < Data ->nCurves; i++) { + if (Data ->TheCurves[i] != NULL) + cmsFreeToneCurve(Data ->TheCurves[i]); + } + } + _cmsFree(mpe ->ContextID, Data ->TheCurves); + _cmsFree(mpe ->ContextID, Data); +} + + +static +void* CurveSetDup(cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; + _cmsStageToneCurvesData* NewElem; + cmsUInt32Number i; + + NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageToneCurvesData)); + if (NewElem == NULL) return NULL; + + NewElem ->nCurves = Data ->nCurves; + NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(mpe ->ContextID, NewElem ->nCurves, sizeof(cmsToneCurve*)); + + if (NewElem ->TheCurves == NULL) goto Error; + + for (i=0; i < NewElem ->nCurves; i++) { + + // Duplicate each curve. It may fail. + NewElem ->TheCurves[i] = cmsDupToneCurve(Data ->TheCurves[i]); + if (NewElem ->TheCurves[i] == NULL) goto Error; + + + } + return (void*) NewElem; + +Error: + + if (NewElem ->TheCurves != NULL) { + for (i=0; i < NewElem ->nCurves; i++) { + if (NewElem ->TheCurves[i]) + cmsFreeToneCurve(NewElem ->TheCurves[i]); + } + } + _cmsFree(mpe ->ContextID, NewElem ->TheCurves); + _cmsFree(mpe ->ContextID, NewElem); + return NULL; +} + + +// Curves == NULL forces identity curves +cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]) +{ + cmsUInt32Number i; + _cmsStageToneCurvesData* NewElem; + cmsStage* NewMPE; + + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCurveSetElemType, nChannels, nChannels, + EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL ); + if (NewMPE == NULL) return NULL; + + NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + NewElem ->nCurves = nChannels; + NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, nChannels, sizeof(cmsToneCurve*)); + if (NewElem ->TheCurves == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + for (i=0; i < nChannels; i++) { + + if (Curves == NULL) { + NewElem ->TheCurves[i] = cmsBuildGamma(ContextID, 1.0); + } + else { + NewElem ->TheCurves[i] = cmsDupToneCurve(Curves[i]); + } + + if (NewElem ->TheCurves[i] == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + } + + return NewMPE; +} + + +// Create a bunch of identity curves +cmsStage* CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels) +{ + cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL); + + if (mpe == NULL) return NULL; + mpe ->Implements = cmsSigIdentityElemType; + return mpe; +} + + +// ************************************************************************************************* +// Type cmsSigMatrixElemType (Matrices) +// ************************************************************************************************* + + +// Special care should be taken here because precision loss. A temporary cmsFloat64Number buffer is being used +static +void EvaluateMatrix(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + cmsUInt32Number i, j; + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + cmsFloat64Number Tmp; + + // Input is already in 0..1.0 notation + for (i=0; i < mpe ->OutputChannels; i++) { + + Tmp = 0; + for (j=0; j < mpe->InputChannels; j++) { + Tmp += In[j] * Data->Double[i*mpe->InputChannels + j]; + } + + if (Data ->Offset != NULL) + Tmp += Data->Offset[i]; + + Out[i] = (cmsFloat32Number) Tmp; + } + + + // Output in 0..1.0 domain +} + + +// Duplicate a yet-existing matrix element +static +void* MatrixElemDup(cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + _cmsStageMatrixData* NewElem; + cmsUInt32Number sz; + + NewElem = (_cmsStageMatrixData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageMatrixData)); + if (NewElem == NULL) return NULL; + + sz = mpe ->InputChannels * mpe ->OutputChannels; + + NewElem ->Double = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, Data ->Double, sz * sizeof(cmsFloat64Number)) ; + + if (Data ->Offset) + NewElem ->Offset = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, + Data ->Offset, mpe -> OutputChannels * sizeof(cmsFloat64Number)) ; + + return (void*) NewElem; +} + + +static +void MatrixElemTypeFree(cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + if (Data == NULL) + return; + if (Data ->Double) + _cmsFree(mpe ->ContextID, Data ->Double); + + if (Data ->Offset) + _cmsFree(mpe ->ContextID, Data ->Offset); + + _cmsFree(mpe ->ContextID, mpe ->Data); +} + + + +cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, + const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset) +{ + cmsUInt32Number i, n; + _cmsStageMatrixData* NewElem; + cmsStage* NewMPE; + + n = Rows * Cols; + + // Check for overflow + if (n == 0) return NULL; + if (n >= UINT_MAX / Cols) return NULL; + if (n >= UINT_MAX / Rows) return NULL; + if (n < Rows || n < Cols) return NULL; + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigMatrixElemType, Cols, Rows, + EvaluateMatrix, MatrixElemDup, MatrixElemTypeFree, NULL ); + if (NewMPE == NULL) return NULL; + + + NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData)); + if (NewElem == NULL) goto Error; + NewMPE->Data = (void*)NewElem; + + NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number)); + if (NewElem->Double == NULL) goto Error; + + for (i=0; i < n; i++) { + NewElem ->Double[i] = Matrix[i]; + } + + if (Offset != NULL) { + + NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number)); + if (NewElem->Offset == NULL) goto Error; + + for (i=0; i < Rows; i++) { + NewElem ->Offset[i] = Offset[i]; + } + } + + return NewMPE; + +Error: + cmsStageFree(NewMPE); + return NULL; +} + + +// ************************************************************************************************* +// Type cmsSigCLutElemType +// ************************************************************************************************* + + +// Evaluate in true floating point +static +void EvaluateCLUTfloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + + Data -> Params ->Interpolation.LerpFloat(In, Out, Data->Params); +} + + +// Convert to 16 bits, evaluate, and back to floating point +static +void EvaluateCLUTfloatIn16(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + cmsUInt16Number In16[MAX_STAGE_CHANNELS], Out16[MAX_STAGE_CHANNELS]; + + _cmsAssert(mpe ->InputChannels <= MAX_STAGE_CHANNELS); + _cmsAssert(mpe ->OutputChannels <= MAX_STAGE_CHANNELS); + + FromFloatTo16(In, In16, mpe ->InputChannels); + Data -> Params ->Interpolation.Lerp16(In16, Out16, Data->Params); + From16ToFloat(Out16, Out, mpe ->OutputChannels); +} + + +// Given an hypercube of b dimensions, with Dims[] number of nodes by dimension, calculate the total amount of nodes +static +cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b) +{ + cmsUInt32Number rv, dim; + + _cmsAssert(Dims != NULL); + + for (rv = 1; b > 0; b--) { + + dim = Dims[b-1]; + if (dim == 0) return 0; // Error + + rv *= dim; + + // Check for overflow + if (rv > UINT_MAX / dim) return 0; + } + + return rv; +} + +static +void* CLUTElemDup(cmsStage* mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + _cmsStageCLutData* NewElem; + + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) return NULL; + + NewElem ->nEntries = Data ->nEntries; + NewElem ->HasFloatValues = Data ->HasFloatValues; + + if (Data ->Tab.T) { + + if (Data ->HasFloatValues) { + NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number)); + if (NewElem ->Tab.TFloat == NULL) + goto Error; + } else { + NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number)); + if (NewElem ->Tab.T == NULL) + goto Error; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(mpe ->ContextID, + Data ->Params ->nSamples, + Data ->Params ->nInputs, + Data ->Params ->nOutputs, + NewElem ->Tab.T, + Data ->Params ->dwFlags); + if (NewElem->Params != NULL) + return (void*) NewElem; + Error: + if (NewElem->Tab.T) + // This works for both types + _cmsFree(mpe ->ContextID, NewElem -> Tab.T); + _cmsFree(mpe ->ContextID, NewElem); + return NULL; +} + + +static +void CLutElemTypeFree(cmsStage* mpe) +{ + + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + + // Already empty + if (Data == NULL) return; + + // This works for both types + if (Data -> Tab.T) + _cmsFree(mpe ->ContextID, Data -> Tab.T); + + _cmsFreeInterpParams(Data ->Params); + _cmsFree(mpe ->ContextID, mpe ->Data); +} + + +// Allocates a 16-bit multidimensional CLUT. This is evaluated at 16-bit precision. Table may have different +// granularity on each dimension. +cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, + const cmsUInt32Number clutPoints[], + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsUInt16Number* Table) +{ + cmsUInt32Number i, n; + _cmsStageCLutData* NewElem; + cmsStage* NewMPE; + + _cmsAssert(clutPoints != NULL); + + if (inputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, + EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL ); + + if (NewMPE == NULL) return NULL; + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); + NewElem -> HasFloatValues = FALSE; + + if (n == 0) { + cmsStageFree(NewMPE); + return NULL; + } + + + NewElem ->Tab.T = (cmsUInt16Number*) _cmsCalloc(ContextID, n, sizeof(cmsUInt16Number)); + if (NewElem ->Tab.T == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + if (Table != NULL) { + for (i=0; i < n; i++) { + NewElem ->Tab.T[i] = Table[i]; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.T, CMS_LERP_FLAGS_16BITS); + if (NewElem ->Params == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + return NewMPE; +} + +cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, + cmsUInt32Number nGridPoints, + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsUInt16Number* Table) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + int i; + + // Our resulting LUT would be same gridpoints on all dimensions + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = nGridPoints; + + return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table); +} + + +cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, + cmsUInt32Number nGridPoints, + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsFloat32Number* Table) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + int i; + + // Our resulting LUT would be same gridpoints on all dimensions + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = nGridPoints; + + return cmsStageAllocCLutFloatGranular(ContextID, Dimensions, inputChan, outputChan, Table); +} + + + +cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table) +{ + cmsUInt32Number i, n; + _cmsStageCLutData* NewElem; + cmsStage* NewMPE; + + _cmsAssert(clutPoints != NULL); + + if (inputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, + EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL); + if (NewMPE == NULL) return NULL; + + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + // There is a potential integer overflow on conputing n and nEntries. + NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); + NewElem -> HasFloatValues = TRUE; + + if (n == 0) { + cmsStageFree(NewMPE); + return NULL; + } + + NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat32Number)); + if (NewElem ->Tab.TFloat == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + if (Table != NULL) { + for (i=0; i < n; i++) { + NewElem ->Tab.TFloat[i] = Table[i]; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT); + if (NewElem ->Params == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + return NewMPE; +} + + +static +int IdentitySampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void * Cargo) +{ + int nChan = *(int*) Cargo; + int i; + + for (i=0; i < nChan; i++) + Out[i] = In[i]; + + return 1; +} + +// Creates an MPE that just copies input to output +cmsStage* CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + cmsStage* mpe ; + int i; + + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = 2; + + mpe = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, nChan, nChan, NULL); + if (mpe == NULL) return NULL; + + if (!cmsStageSampleCLut16bit(mpe, IdentitySampler, &nChan, 0)) { + cmsStageFree(mpe); + return NULL; + } + + mpe ->Implements = cmsSigIdentityElemType; + return mpe; +} + + + +// Quantize a value 0 <= i < MaxSamples to 0..0xffff +cmsUInt16Number CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples) +{ + cmsFloat64Number x; + + x = ((cmsFloat64Number) i * 65535.) / (cmsFloat64Number) (MaxSamples - 1); + return _cmsQuickSaturateWord(x); +} + + +// This routine does a sweep on whole input space, and calls its callback +// function on knots. returns TRUE if all ok, FALSE otherwise. +cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags) +{ + int i, t, index, rest; + cmsUInt32Number nTotalPoints; + cmsUInt32Number nInputs, nOutputs; + cmsUInt32Number* nSamples; + cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; + _cmsStageCLutData* clut; + + if (mpe == NULL) return FALSE; + + clut = (_cmsStageCLutData*) mpe->Data; + + if (clut == NULL) return FALSE; + + nSamples = clut->Params ->nSamples; + nInputs = clut->Params ->nInputs; + nOutputs = clut->Params ->nOutputs; + + if (nInputs <= 0) return FALSE; + if (nOutputs <= 0) return FALSE; + if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; + if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; + + memset(In, 0, sizeof(In)); + memset(Out, 0, sizeof(Out)); + + nTotalPoints = CubeSize(nSamples, nInputs); + if (nTotalPoints == 0) return FALSE; + + index = 0; + for (i = 0; i < (int) nTotalPoints; i++) { + + rest = i; + for (t = (int)nInputs - 1; t >= 0; --t) { + + cmsUInt32Number Colorant = rest % nSamples[t]; + + rest /= nSamples[t]; + + In[t] = _cmsQuantizeVal(Colorant, nSamples[t]); + } + + if (clut ->Tab.T != NULL) { + for (t = 0; t < (int)nOutputs; t++) + Out[t] = clut->Tab.T[index + t]; + } + + if (!Sampler(In, Out, Cargo)) + return FALSE; + + if (!(dwFlags & SAMPLER_INSPECT)) { + + if (clut ->Tab.T != NULL) { + for (t=0; t < (int) nOutputs; t++) + clut->Tab.T[index + t] = Out[t]; + } + } + + index += nOutputs; + } + + return TRUE; +} + +// Same as anterior, but for floating point +cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags) +{ + int i, t, index, rest; + cmsUInt32Number nTotalPoints; + cmsUInt32Number nInputs, nOutputs; + cmsUInt32Number* nSamples; + cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; + _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data; + + nSamples = clut->Params ->nSamples; + nInputs = clut->Params ->nInputs; + nOutputs = clut->Params ->nOutputs; + + if (nInputs <= 0) return FALSE; + if (nOutputs <= 0) return FALSE; + if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; + if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; + + nTotalPoints = CubeSize(nSamples, nInputs); + if (nTotalPoints == 0) return FALSE; + + index = 0; + for (i = 0; i < (int)nTotalPoints; i++) { + + rest = i; + for (t = (int) nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % nSamples[t]; + + rest /= nSamples[t]; + + In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, nSamples[t]) / 65535.0); + } + + if (clut ->Tab.TFloat != NULL) { + for (t=0; t < (int) nOutputs; t++) + Out[t] = clut->Tab.TFloat[index + t]; + } + + if (!Sampler(In, Out, Cargo)) + return FALSE; + + if (!(dwFlags & SAMPLER_INSPECT)) { + + if (clut ->Tab.TFloat != NULL) { + for (t=0; t < (int) nOutputs; t++) + clut->Tab.TFloat[index + t] = Out[t]; + } + } + + index += nOutputs; + } + + return TRUE; +} + + + +// This routine does a sweep on whole input space, and calls its callback +// function on knots. returns TRUE if all ok, FALSE otherwise. +cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLER16 Sampler, void * Cargo) +{ + int i, t, rest; + cmsUInt32Number nTotalPoints; + cmsUInt16Number In[cmsMAXCHANNELS]; + + if (nInputs >= cmsMAXCHANNELS) return FALSE; + + nTotalPoints = CubeSize(clutPoints, nInputs); + if (nTotalPoints == 0) return FALSE; + + for (i = 0; i < (int) nTotalPoints; i++) { + + rest = i; + for (t = (int) nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % clutPoints[t]; + + rest /= clutPoints[t]; + In[t] = _cmsQuantizeVal(Colorant, clutPoints[t]); + + } + + if (!Sampler(In, NULL, Cargo)) + return FALSE; + } + + return TRUE; +} + +cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLERFLOAT Sampler, void * Cargo) +{ + int i, t, rest; + cmsUInt32Number nTotalPoints; + cmsFloat32Number In[cmsMAXCHANNELS]; + + if (nInputs >= cmsMAXCHANNELS) return FALSE; + + nTotalPoints = CubeSize(clutPoints, nInputs); + if (nTotalPoints == 0) return FALSE; + + for (i = 0; i < (int) nTotalPoints; i++) { + + rest = i; + for (t = (int) nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % clutPoints[t]; + + rest /= clutPoints[t]; + In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, clutPoints[t]) / 65535.0); + + } + + if (!Sampler(In, NULL, Cargo)) + return FALSE; + } + + return TRUE; +} + +// ******************************************************************************** +// Type cmsSigLab2XYZElemType +// ******************************************************************************** + + +static +void EvaluateLab2XYZ(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + cmsCIELab Lab; + cmsCIEXYZ XYZ; + const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; + + // V4 rules + Lab.L = In[0] * 100.0; + Lab.a = In[1] * 255.0 - 128.0; + Lab.b = In[2] * 255.0 - 128.0; + + cmsLab2XYZ(NULL, &XYZ, &Lab); + + // From XYZ, range 0..19997 to 0..1.0, note that 1.99997 comes from 0xffff + // encoded as 1.15 fixed point, so 1 + (32767.0 / 32768.0) + + Out[0] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.X / XYZadj); + Out[1] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Y / XYZadj); + Out[2] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Z / XYZadj); + return; + + cmsUNUSED_PARAMETER(mpe); +} + + +// No dup or free routines needed, as the structure has no pointers in it. +cmsStage* CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL); +} + +// ******************************************************************************** + +// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable +// number of gridpoints that would make exact match. However, a prelinearization +// of 258 entries, would map 0xFF00 exactly on entry 257, and this is good to avoid scum dot. +// Almost all what we need but unfortunately, the rest of entries should be scaled by +// (255*257/256) and this is not exact. + +cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID) +{ + cmsStage* mpe; + cmsToneCurve* LabTable[3]; + int i, j; + + LabTable[0] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + LabTable[1] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + LabTable[2] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + + for (j=0; j < 3; j++) { + + if (LabTable[j] == NULL) { + cmsFreeToneCurveTriple(LabTable); + return NULL; + } + + // We need to map * (0xffff / 0xff00), that's same as (257 / 256) + // So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256); + for (i=0; i < 257; i++) { + + LabTable[j]->Table16[i] = (cmsUInt16Number) ((i * 0xffff + 0x80) >> 8); + } + + LabTable[j] ->Table16[257] = 0xffff; + } + + mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable); + cmsFreeToneCurveTriple(LabTable); + + if (mpe == NULL) return NULL; + mpe ->Implements = cmsSigLabV2toV4; + return mpe; +} + +// ******************************************************************************** + +// Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles +cmsStage* CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID) +{ + static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0, + 0, 65535.0/65280.0, 0, + 0, 0, 65535.0/65280.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V2ToV4, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLabV2toV4; + return mpe; +} + + +// Reverse direction +cmsStage* CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID) +{ + static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0, + 0, 65280.0/65535.0, 0, + 0, 0, 65280.0/65535.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V4ToV2, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLabV4toV2; + return mpe; +} + + +// To Lab to float. Note that the MPE gives numbers in normal Lab range +// and we need 0..1.0 range for the formatters +// L* : 0...100 => 0...1.0 (L* / 100) +// ab* : -128..+127 to 0..1 ((ab* + 128) / 255) + +cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID) +{ + static const cmsFloat64Number a1[] = { + 1.0/100.0, 0, 0, + 0, 1.0/255.0, 0, + 0, 0, 1.0/255.0 + }; + + static const cmsFloat64Number o1[] = { + 0, + 128.0/255.0, + 128.0/255.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLab2FloatPCS; + return mpe; +} + +// Fom XYZ to floating point PCS +cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID) +{ +#define n (32768.0/65535.0) + static const cmsFloat64Number a1[] = { + n, 0, 0, + 0, n, 0, + 0, 0, n + }; +#undef n + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigXYZ2FloatPCS; + return mpe; +} + +cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID) +{ + static const cmsFloat64Number a1[] = { + 100.0, 0, 0, + 0, 255.0, 0, + 0, 0, 255.0 + }; + + static const cmsFloat64Number o1[] = { + 0, + -128.0, + -128.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigFloatPCS2Lab; + return mpe; +} + +cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID) +{ +#define n (65535.0/32768.0) + + static const cmsFloat64Number a1[] = { + n, 0, 0, + 0, n, 0, + 0, 0, n + }; +#undef n + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigFloatPCS2XYZ; + return mpe; +} + +// Clips values smaller than zero +static +void Clipper(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsUInt32Number i; + for (i = 0; i < mpe->InputChannels; i++) { + + cmsFloat32Number n = In[i]; + Out[i] = n < 0 ? 0 : n; + } +} + +cmsStage* _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigClipNegativesElemType, + nChannels, nChannels, Clipper, NULL, NULL, NULL); +} + +// ******************************************************************************** +// Type cmsSigXYZ2LabElemType +// ******************************************************************************** + +static +void EvaluateXYZ2Lab(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsCIELab Lab; + cmsCIEXYZ XYZ; + const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; + + // From 0..1.0 to XYZ + + XYZ.X = In[0] * XYZadj; + XYZ.Y = In[1] * XYZadj; + XYZ.Z = In[2] * XYZadj; + + cmsXYZ2Lab(NULL, &Lab, &XYZ); + + // From V4 Lab to 0..1.0 + + Out[0] = (cmsFloat32Number) (Lab.L / 100.0); + Out[1] = (cmsFloat32Number) ((Lab.a + 128.0) / 255.0); + Out[2] = (cmsFloat32Number) ((Lab.b + 128.0) / 255.0); + return; + + cmsUNUSED_PARAMETER(mpe); +} + +cmsStage* CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL); + +} + +// ******************************************************************************** + +// For v4, S-Shaped curves are placed in a/b axis to increase resolution near gray + +cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID) +{ + cmsToneCurve* LabTable[3]; + cmsFloat64Number Params[1] = {2.4} ; + + LabTable[0] = cmsBuildGamma(ContextID, 1.0); + LabTable[1] = cmsBuildParametricToneCurve(ContextID, 108, Params); + LabTable[2] = cmsBuildParametricToneCurve(ContextID, 108, Params); + + return cmsStageAllocToneCurves(ContextID, 3, LabTable); +} + + +// Free a single MPE +void CMSEXPORT cmsStageFree(cmsStage* mpe) +{ + if (mpe ->FreePtr) + mpe ->FreePtr(mpe); + + _cmsFree(mpe ->ContextID, mpe); +} + + +cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe) +{ + return mpe ->InputChannels; +} + +cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe) +{ + return mpe ->OutputChannels; +} + +cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe) +{ + return mpe -> Type; +} + +void* CMSEXPORT cmsStageData(const cmsStage* mpe) +{ + return mpe -> Data; +} + +cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe) +{ + return mpe -> Next; +} + + +// Duplicates an MPE +cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe) +{ + cmsStage* NewMPE; + + if (mpe == NULL) return NULL; + NewMPE = _cmsStageAllocPlaceholder(mpe ->ContextID, + mpe ->Type, + mpe ->InputChannels, + mpe ->OutputChannels, + mpe ->EvalPtr, + mpe ->DupElemPtr, + mpe ->FreePtr, + NULL); + if (NewMPE == NULL) return NULL; + + NewMPE ->Implements = mpe ->Implements; + + if (mpe ->DupElemPtr) { + + NewMPE ->Data = mpe ->DupElemPtr(mpe); + + if (NewMPE->Data == NULL) { + + cmsStageFree(NewMPE); + return NULL; + } + + } else { + + NewMPE ->Data = NULL; + } + + return NewMPE; +} + + +// *********************************************************************************************************** + +// This function sets up the channel count +static +cmsBool BlessLUT(cmsPipeline* lut) +{ + // We can set the input/output channels only if we have elements. + if (lut ->Elements != NULL) { + + cmsStage* prev; + cmsStage* next; + cmsStage* First; + cmsStage* Last; + + First = cmsPipelineGetPtrToFirstStage(lut); + Last = cmsPipelineGetPtrToLastStage(lut); + + if (First == NULL || Last == NULL) return FALSE; + + lut->InputChannels = First->InputChannels; + lut->OutputChannels = Last->OutputChannels; + + // Check chain consistency + prev = First; + next = prev->Next; + + while (next != NULL) + { + if (next->InputChannels != prev->OutputChannels) + return FALSE; + + next = next->Next; + prev = prev->Next; + } +} + + return TRUE; +} + + +// Default to evaluate the LUT on 16 bit-basis. Precision is retained. +static +void _LUTeval16(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER const void* D) +{ + cmsPipeline* lut = (cmsPipeline*) D; + cmsStage *mpe; + cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS]; + int Phase = 0, NextPhase; + + From16ToFloat(In, &Storage[Phase][0], lut ->InputChannels); + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NextPhase = Phase ^ 1; + mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); + Phase = NextPhase; + } + + + FromFloatTo16(&Storage[Phase][0], Out, lut ->OutputChannels); +} + + + +// Does evaluate the LUT on cmsFloat32Number-basis. +static +void _LUTevalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const void* D) +{ + cmsPipeline* lut = (cmsPipeline*) D; + cmsStage *mpe; + cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS]; + int Phase = 0, NextPhase; + + memmove(&Storage[Phase][0], In, lut ->InputChannels * sizeof(cmsFloat32Number)); + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NextPhase = Phase ^ 1; + mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); + Phase = NextPhase; + } + + memmove(Out, &Storage[Phase][0], lut ->OutputChannels * sizeof(cmsFloat32Number)); +} + + +// LUT Creation & Destruction +cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels) +{ + cmsPipeline* NewLUT; + + // A value of zero in channels is allowed as placeholder + if (InputChannels >= cmsMAXCHANNELS || + OutputChannels >= cmsMAXCHANNELS) return NULL; + + NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline)); + if (NewLUT == NULL) return NULL; + + NewLUT -> InputChannels = InputChannels; + NewLUT -> OutputChannels = OutputChannels; + + NewLUT ->Eval16Fn = _LUTeval16; + NewLUT ->EvalFloatFn = _LUTevalFloat; + NewLUT ->DupDataFn = NULL; + NewLUT ->FreeDataFn = NULL; + NewLUT ->Data = NewLUT; + NewLUT ->ContextID = ContextID; + + if (!BlessLUT(NewLUT)) + { + _cmsFree(ContextID, NewLUT); + return NULL; + } + + return NewLUT; +} + +cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->ContextID; +} + +cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->InputChannels; +} + +cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->OutputChannels; +} + +// Free a profile elements LUT +void CMSEXPORT cmsPipelineFree(cmsPipeline* lut) +{ + cmsStage *mpe, *Next; + + if (lut == NULL) return; + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = Next) { + + Next = mpe ->Next; + cmsStageFree(mpe); + } + + if (lut ->FreeDataFn) lut ->FreeDataFn(lut ->ContextID, lut ->Data); + + _cmsFree(lut ->ContextID, lut); +} + + +// Default to evaluate the LUT on 16 bit-basis. +void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + lut ->Eval16Fn(In, Out, lut->Data); +} + + +// Does evaluate the LUT on cmsFloat32Number-basis. +void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + lut ->EvalFloatFn(In, Out, lut); +} + + + +// Duplicates a LUT +cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut) +{ + cmsPipeline* NewLUT; + cmsStage *NewMPE, *Anterior = NULL, *mpe; + cmsBool First = TRUE; + + if (lut == NULL) return NULL; + + NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels); + if (NewLUT == NULL) return NULL; + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NewMPE = cmsStageDup(mpe); + + if (NewMPE == NULL) { + cmsPipelineFree(NewLUT); + return NULL; + } + + if (First) { + NewLUT ->Elements = NewMPE; + First = FALSE; + } + else { + if (Anterior != NULL) + Anterior ->Next = NewMPE; + } + + Anterior = NewMPE; + } + + NewLUT ->Eval16Fn = lut ->Eval16Fn; + NewLUT ->EvalFloatFn = lut ->EvalFloatFn; + NewLUT ->DupDataFn = lut ->DupDataFn; + NewLUT ->FreeDataFn = lut ->FreeDataFn; + + if (NewLUT ->DupDataFn != NULL) + NewLUT ->Data = NewLUT ->DupDataFn(lut ->ContextID, lut->Data); + + + NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits; + + if (!BlessLUT(NewLUT)) + { + _cmsFree(lut->ContextID, NewLUT); + return NULL; + } + + return NewLUT; +} + + +int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe) +{ + cmsStage* Anterior = NULL, *pt; + + if (lut == NULL || mpe == NULL) + return FALSE; + + switch (loc) { + + case cmsAT_BEGIN: + mpe ->Next = lut ->Elements; + lut ->Elements = mpe; + break; + + case cmsAT_END: + + if (lut ->Elements == NULL) + lut ->Elements = mpe; + else { + + for (pt = lut ->Elements; + pt != NULL; + pt = pt -> Next) Anterior = pt; + + Anterior ->Next = mpe; + mpe ->Next = NULL; + } + break; + default:; + return FALSE; + } + + return BlessLUT(lut); +} + +// Unlink an element and return the pointer to it +void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe) +{ + cmsStage *Anterior, *pt, *Last; + cmsStage *Unlinked = NULL; + + + // If empty LUT, there is nothing to remove + if (lut ->Elements == NULL) { + if (mpe) *mpe = NULL; + return; + } + + // On depending on the strategy... + switch (loc) { + + case cmsAT_BEGIN: + { + cmsStage* elem = lut ->Elements; + + lut ->Elements = elem -> Next; + elem ->Next = NULL; + Unlinked = elem; + + } + break; + + case cmsAT_END: + Anterior = Last = NULL; + for (pt = lut ->Elements; + pt != NULL; + pt = pt -> Next) { + Anterior = Last; + Last = pt; + } + + Unlinked = Last; // Next already points to NULL + + // Truncate the chain + if (Anterior) + Anterior ->Next = NULL; + else + lut ->Elements = NULL; + break; + default:; + } + + if (mpe) + *mpe = Unlinked; + else + cmsStageFree(Unlinked); + + // May fail, but we ignore it + BlessLUT(lut); +} + + +// Concatenate two LUT into a new single one +cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2) +{ + cmsStage* mpe; + + // If both LUTS does not have elements, we need to inherit + // the number of channels + if (l1 ->Elements == NULL && l2 ->Elements == NULL) { + l1 ->InputChannels = l2 ->InputChannels; + l1 ->OutputChannels = l2 ->OutputChannels; + } + + // Cat second + for (mpe = l2 ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + // We have to dup each element + if (!cmsPipelineInsertStage(l1, cmsAT_END, cmsStageDup(mpe))) + return FALSE; + } + + return BlessLUT(l1); +} + + +cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On) +{ + cmsBool Anterior = lut ->SaveAs8Bits; + + lut ->SaveAs8Bits = On; + return Anterior; +} + + +cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut) +{ + return lut ->Elements; +} + +cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut) +{ + cmsStage *mpe, *Anterior = NULL; + + for (mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) + Anterior = mpe; + + return Anterior; +} + +cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut) +{ + cmsStage *mpe; + cmsUInt32Number n; + + for (n=0, mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) + n++; + + return n; +} + +// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional +// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. +void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, + _cmsPipelineEval16Fn Eval16, + void* PrivateData, + _cmsFreeUserDataFn FreePrivateDataFn, + _cmsDupUserDataFn DupPrivateDataFn) +{ + + Lut ->Eval16Fn = Eval16; + Lut ->DupDataFn = DupPrivateDataFn; + Lut ->FreeDataFn = FreePrivateDataFn; + Lut ->Data = PrivateData; +} + + +// ----------------------------------------------------------- Reverse interpolation +// Here's how it goes. The derivative Df(x) of the function f is the linear +// transformation that best approximates f near the point x. It can be represented +// by a matrix A whose entries are the partial derivatives of the components of f +// with respect to all the coordinates. This is know as the Jacobian +// +// The best linear approximation to f is given by the matrix equation: +// +// y-y0 = A (x-x0) +// +// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this +// linear approximation will give a "better guess" for the zero of f. Thus let y=0, +// and since y0=f(x0) one can solve the above equation for x. This leads to the +// Newton's method formula: +// +// xn+1 = xn - A-1 f(xn) +// +// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the +// fashion described above. Iterating this will give better and better approximations +// if you have a "good enough" initial guess. + + +#define JACOBIAN_EPSILON 0.001f +#define INVERSION_MAX_ITERATIONS 30 + +// Increment with reflexion on boundary +static +void IncDelta(cmsFloat32Number *Val) +{ + if (*Val < (1.0 - JACOBIAN_EPSILON)) + + *Val += JACOBIAN_EPSILON; + + else + *Val -= JACOBIAN_EPSILON; + +} + + + +// Euclidean distance between two vectors of n elements each one +static +cmsFloat32Number EuclideanDistance(cmsFloat32Number a[], cmsFloat32Number b[], int n) +{ + cmsFloat32Number sum = 0; + int i; + + for (i=0; i < n; i++) { + cmsFloat32Number dif = b[i] - a[i]; + sum += dif * dif; + } + + return sqrtf(sum); +} + + +// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT. Uses Newton method +// +// x1 <- x - [J(x)]^-1 * f(x) +// +// lut: The LUT on where to do the search +// Target: LabK, 3 values of Lab plus destination K which is fixed +// Result: The obtained CMYK +// Hint: Location where begin the search + +cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[], + cmsFloat32Number Result[], + cmsFloat32Number Hint[], + const cmsPipeline* lut) +{ + cmsUInt32Number i, j; + cmsFloat64Number error, LastError = 1E20; + cmsFloat32Number fx[4], x[4], xd[4], fxd[4]; + cmsVEC3 tmp, tmp2; + cmsMAT3 Jacobian; + + // Only 3->3 and 4->3 are supported + if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE; + if (lut ->OutputChannels != 3) return FALSE; + + // Take the hint as starting point if specified + if (Hint == NULL) { + + // Begin at any point, we choose 1/3 of CMY axis + x[0] = x[1] = x[2] = 0.3f; + } + else { + + // Only copy 3 channels from hint... + for (j=0; j < 3; j++) + x[j] = Hint[j]; + } + + // If Lut is 4-dimensions, then grab target[3], which is fixed + if (lut ->InputChannels == 4) { + x[3] = Target[3]; + } + else x[3] = 0; // To keep lint happy + + + // Iterate + for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) { + + // Get beginning fx + cmsPipelineEvalFloat(x, fx, lut); + + // Compute error + error = EuclideanDistance(fx, Target, 3); + + // If not convergent, return last safe value + if (error >= LastError) + break; + + // Keep latest values + LastError = error; + for (j=0; j < lut ->InputChannels; j++) + Result[j] = x[j]; + + // Found an exact match? + if (error <= 0) + break; + + // Obtain slope (the Jacobian) + for (j = 0; j < 3; j++) { + + xd[0] = x[0]; + xd[1] = x[1]; + xd[2] = x[2]; + xd[3] = x[3]; // Keep fixed channel + + IncDelta(&xd[j]); + + cmsPipelineEvalFloat(xd, fxd, lut); + + Jacobian.v[0].n[j] = ((fxd[0] - fx[0]) / JACOBIAN_EPSILON); + Jacobian.v[1].n[j] = ((fxd[1] - fx[1]) / JACOBIAN_EPSILON); + Jacobian.v[2].n[j] = ((fxd[2] - fx[2]) / JACOBIAN_EPSILON); + } + + // Solve system + tmp2.n[0] = fx[0] - Target[0]; + tmp2.n[1] = fx[1] - Target[1]; + tmp2.n[2] = fx[2] - Target[2]; + + if (!_cmsMAT3solve(&tmp, &Jacobian, &tmp2)) + return FALSE; + + // Move our guess + x[0] -= (cmsFloat32Number) tmp.n[0]; + x[1] -= (cmsFloat32Number) tmp.n[1]; + x[2] -= (cmsFloat32Number) tmp.n[2]; + + // Some clipping.... + for (j=0; j < 3; j++) { + if (x[j] < 0) x[j] = 0; + else + if (x[j] > 1.0) x[j] = 1.0; + } + } + + return TRUE; +} + + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsmd5.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsmd5.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsmd5.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsmd5.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,342 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- + + +#include "lcms2_internal.h" + +#ifdef CMS_USE_BIG_ENDIAN + +static +void byteReverse(cmsUInt8Number * buf, cmsUInt32Number longs) +{ + do { + + cmsUInt32Number t = _cmsAdjustEndianess32(*(cmsUInt32Number *) buf); + *(cmsUInt32Number *) buf = t; + buf += sizeof(cmsUInt32Number); + + } while (--longs); + +} + +#else +#define byteReverse(buf, len) +#endif + + +typedef struct { + + cmsUInt32Number buf[4]; + cmsUInt32Number bits[2]; + cmsUInt8Number in[64]; + cmsContext ContextID; + +} _cmsMD5; + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + + +static +void cmsMD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16]) +{ + CMSREGISTER cmsUInt32Number a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + + +// Create a MD5 object + +cmsHANDLE CMSEXPORT cmsMD5alloc(cmsContext ContextID) +{ + _cmsMD5* ctx = (_cmsMD5*) _cmsMallocZero(ContextID, sizeof(_cmsMD5)); + if (ctx == NULL) return NULL; + + ctx ->ContextID = ContextID; + + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; + + return (cmsHANDLE) ctx; +} + +void CMSEXPORT cmsMD5add(cmsHANDLE Handle, const cmsUInt8Number* buf, cmsUInt32Number len) +{ + _cmsMD5* ctx = (_cmsMD5*) Handle; + cmsUInt32Number t; + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + (len << 3)) < t) + ctx->bits[1]++; + + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; + + if (t) { + + cmsUInt8Number *p = (cmsUInt8Number *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memmove(p, buf, len); + return; + } + + memmove(p, buf, t); + byteReverse(ctx->in, 16); + + cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + buf += t; + len -= t; + } + + while (len >= 64) { + memmove(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + buf += 64; + len -= 64; + } + + memmove(ctx->in, buf, len); +} + +// Destroy the object and return the checksum +void CMSEXPORT cmsMD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle) +{ + _cmsMD5* ctx = (_cmsMD5*) Handle; + cmsUInt32Number count; + cmsUInt8Number *p; + + count = (ctx->bits[0] >> 3) & 0x3F; + + p = ctx->in + count; + *p++ = 0x80; + + count = 64 - 1 - count; + + if (count < 8) { + + memset(p, 0, count); + byteReverse(ctx->in, 16); + cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + + memset(ctx->in, 0, 56); + } else { + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + ((cmsUInt32Number *) ctx->in)[14] = ctx->bits[0]; + ((cmsUInt32Number *) ctx->in)[15] = ctx->bits[1]; + + cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + + byteReverse((cmsUInt8Number *) ctx->buf, 4); + memmove(ProfileID ->ID8, ctx->buf, 16); + + _cmsFree(ctx ->ContextID, ctx); +} + + + +// Assuming io points to an ICC profile, compute and store MD5 checksum +// In the header, rendering intentent, attributes and ID should be set to zero +// before computing MD5 checksum (per 6.1.13 in ICC spec) + +cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile) +{ + cmsContext ContextID; + cmsUInt32Number BytesNeeded; + cmsUInt8Number* Mem = NULL; + cmsHANDLE MD5 = NULL; + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + _cmsICCPROFILE Keep; + + _cmsAssert(hProfile != NULL); + + ContextID = cmsGetProfileContextID(hProfile); + + // Save a copy of the profile header + memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); + + // Set RI, attributes and ID + memset(&Icc ->attributes, 0, sizeof(Icc ->attributes)); + Icc ->RenderingIntent = 0; + memset(&Icc ->ProfileID, 0, sizeof(Icc ->ProfileID)); + + // Compute needed storage + if (!cmsSaveProfileToMem(hProfile, NULL, &BytesNeeded)) goto Error; + + // Allocate memory + Mem = (cmsUInt8Number*) _cmsMalloc(ContextID, BytesNeeded); + if (Mem == NULL) goto Error; + + // Save to temporary storage + if (!cmsSaveProfileToMem(hProfile, Mem, &BytesNeeded)) goto Error; + + // Create MD5 object + MD5 = cmsMD5alloc(ContextID); + if (MD5 == NULL) goto Error; + + // Add all bytes + cmsMD5add(MD5, Mem, BytesNeeded); + + // Temp storage is no longer needed + _cmsFree(ContextID, Mem); + + // Restore header + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + + // And store the ID + cmsMD5finish(&Icc ->ProfileID, MD5); + return TRUE; + +Error: + + // Free resources as something went wrong + // "MD5" cannot be other than NULL here, so no need to free it + if (Mem != NULL) _cmsFree(ContextID, Mem); + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + return FALSE; +} + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsmtrx.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsmtrx.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsmtrx.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsmtrx.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,205 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +#define DSWAP(x, y) {cmsFloat64Number tmp = (x); (x)=(y); (y)=tmp;} + + +// Initiate a vector +void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z) +{ + r -> n[VX] = x; + r -> n[VY] = y; + r -> n[VZ] = z; +} + +// Vector subtraction +void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b) +{ + r -> n[VX] = a -> n[VX] - b -> n[VX]; + r -> n[VY] = a -> n[VY] - b -> n[VY]; + r -> n[VZ] = a -> n[VZ] - b -> n[VZ]; +} + +// Vector cross product +void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v) +{ + r ->n[VX] = u->n[VY] * v->n[VZ] - v->n[VY] * u->n[VZ]; + r ->n[VY] = u->n[VZ] * v->n[VX] - v->n[VZ] * u->n[VX]; + r ->n[VZ] = u->n[VX] * v->n[VY] - v->n[VX] * u->n[VY]; +} + +// Vector dot product +cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v) +{ + return u->n[VX] * v->n[VX] + u->n[VY] * v->n[VY] + u->n[VZ] * v->n[VZ]; +} + +// Euclidean length +cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a) +{ + return sqrt(a ->n[VX] * a ->n[VX] + + a ->n[VY] * a ->n[VY] + + a ->n[VZ] * a ->n[VZ]); +} + +// Euclidean distance +cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b) +{ + cmsFloat64Number d1 = a ->n[VX] - b ->n[VX]; + cmsFloat64Number d2 = a ->n[VY] - b ->n[VY]; + cmsFloat64Number d3 = a ->n[VZ] - b ->n[VZ]; + + return sqrt(d1*d1 + d2*d2 + d3*d3); +} + + + +// 3x3 Identity +void CMSEXPORT _cmsMAT3identity(cmsMAT3* a) +{ + _cmsVEC3init(&a-> v[0], 1.0, 0.0, 0.0); + _cmsVEC3init(&a-> v[1], 0.0, 1.0, 0.0); + _cmsVEC3init(&a-> v[2], 0.0, 0.0, 1.0); +} + +static +cmsBool CloseEnough(cmsFloat64Number a, cmsFloat64Number b) +{ + return fabs(b - a) < (1.0 / 65535.0); +} + + +cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a) +{ + cmsMAT3 Identity; + int i, j; + + _cmsMAT3identity(&Identity); + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + if (!CloseEnough(a ->v[i].n[j], Identity.v[i].n[j])) return FALSE; + + return TRUE; +} + + +// Multiply two matrices +void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b) +{ +#define ROWCOL(i, j) \ + a->v[i].n[0]*b->v[0].n[j] + a->v[i].n[1]*b->v[1].n[j] + a->v[i].n[2]*b->v[2].n[j] + + _cmsVEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2)); + _cmsVEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2)); + _cmsVEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2)); + +#undef ROWCOL //(i, j) +} + + + +// Inverse of a matrix b = a^(-1) +cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b) +{ + cmsFloat64Number det, c0, c1, c2; + + c0 = a -> v[1].n[1]*a -> v[2].n[2] - a -> v[1].n[2]*a -> v[2].n[1]; + c1 = -a -> v[1].n[0]*a -> v[2].n[2] + a -> v[1].n[2]*a -> v[2].n[0]; + c2 = a -> v[1].n[0]*a -> v[2].n[1] - a -> v[1].n[1]*a -> v[2].n[0]; + + det = a -> v[0].n[0]*c0 + a -> v[0].n[1]*c1 + a -> v[0].n[2]*c2; + + if (fabs(det) < MATRIX_DET_TOLERANCE) return FALSE; // singular matrix; can't invert + + b -> v[0].n[0] = c0/det; + b -> v[0].n[1] = (a -> v[0].n[2]*a -> v[2].n[1] - a -> v[0].n[1]*a -> v[2].n[2])/det; + b -> v[0].n[2] = (a -> v[0].n[1]*a -> v[1].n[2] - a -> v[0].n[2]*a -> v[1].n[1])/det; + b -> v[1].n[0] = c1/det; + b -> v[1].n[1] = (a -> v[0].n[0]*a -> v[2].n[2] - a -> v[0].n[2]*a -> v[2].n[0])/det; + b -> v[1].n[2] = (a -> v[0].n[2]*a -> v[1].n[0] - a -> v[0].n[0]*a -> v[1].n[2])/det; + b -> v[2].n[0] = c2/det; + b -> v[2].n[1] = (a -> v[0].n[1]*a -> v[2].n[0] - a -> v[0].n[0]*a -> v[2].n[1])/det; + b -> v[2].n[2] = (a -> v[0].n[0]*a -> v[1].n[1] - a -> v[0].n[1]*a -> v[1].n[0])/det; + + return TRUE; +} + + +// Solve a system in the form Ax = b +cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b) +{ + cmsMAT3 m, a_1; + + memmove(&m, a, sizeof(cmsMAT3)); + + if (!_cmsMAT3inverse(&m, &a_1)) return FALSE; // Singular matrix + + _cmsMAT3eval(x, &a_1, b); + return TRUE; +} + +// Evaluate a vector across a matrix +void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v) +{ + r->n[VX] = a->v[0].n[VX]*v->n[VX] + a->v[0].n[VY]*v->n[VY] + a->v[0].n[VZ]*v->n[VZ]; + r->n[VY] = a->v[1].n[VX]*v->n[VX] + a->v[1].n[VY]*v->n[VY] + a->v[1].n[VZ]*v->n[VZ]; + r->n[VZ] = a->v[2].n[VX]*v->n[VX] + a->v[2].n[VY]*v->n[VY] + a->v[2].n[VZ]*v->n[VZ]; +} + + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsnamed.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsnamed.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsnamed.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsnamed.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1009 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Multilocalized unicode objects. That is an attempt to encapsulate i18n. + + +// Allocates an empty multi localizad unicode object +cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems) +{ + cmsMLU* mlu; + + // nItems should be positive if given + if (nItems <= 0) nItems = 2; + + // Create the container + mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU)); + if (mlu == NULL) return NULL; + + mlu ->ContextID = ContextID; + + // Create entry array + mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry)); + if (mlu ->Entries == NULL) { + _cmsFree(ContextID, mlu); + return NULL; + } + + // Ok, keep indexes up to date + mlu ->AllocatedEntries = nItems; + mlu ->UsedEntries = 0; + + return mlu; +} + + +// Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two. +static +cmsBool GrowMLUpool(cmsMLU* mlu) +{ + cmsUInt32Number size; + void *NewPtr; + + // Sanity check + if (mlu == NULL) return FALSE; + + if (mlu ->PoolSize == 0) + size = 256; + else + size = mlu ->PoolSize * 2; + + // Check for overflow + if (size < mlu ->PoolSize) return FALSE; + + // Reallocate the pool + NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size); + if (NewPtr == NULL) return FALSE; + + + mlu ->MemPool = NewPtr; + mlu ->PoolSize = size; + + return TRUE; +} + + +// Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two. +static +cmsBool GrowMLUtable(cmsMLU* mlu) +{ + cmsUInt32Number AllocatedEntries; + _cmsMLUentry *NewPtr; + + // Sanity check + if (mlu == NULL) return FALSE; + + AllocatedEntries = mlu ->AllocatedEntries * 2; + + // Check for overflow + if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE; + + // Reallocate the memory + NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry)); + if (NewPtr == NULL) return FALSE; + + mlu ->Entries = NewPtr; + mlu ->AllocatedEntries = AllocatedEntries; + + return TRUE; +} + + +// Search for a specific entry in the structure. Language and Country are used. +static +int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) +{ + cmsUInt32Number i; + + // Sanity check + if (mlu == NULL) return -1; + + // Iterate whole table + for (i=0; i < mlu ->UsedEntries; i++) { + + if (mlu ->Entries[i].Country == CountryCode && + mlu ->Entries[i].Language == LanguageCode) return (int) i; + } + + // Not found + return -1; +} + +// Add a block of characters to the intended MLU. Language and country are specified. +// Only one entry for Language/country pair is allowed. +static +cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block, + cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) +{ + cmsUInt32Number Offset; + cmsUInt8Number* Ptr; + + // Sanity check + if (mlu == NULL) return FALSE; + + // Is there any room available? + if (mlu ->UsedEntries >= mlu ->AllocatedEntries) { + if (!GrowMLUtable(mlu)) return FALSE; + } + + // Only one ASCII string + if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed! + + // Check for size + while ((mlu ->PoolSize - mlu ->PoolUsed) < size) { + + if (!GrowMLUpool(mlu)) return FALSE; + } + + Offset = mlu ->PoolUsed; + + Ptr = (cmsUInt8Number*) mlu ->MemPool; + if (Ptr == NULL) return FALSE; + + // Set the entry + memmove(Ptr + Offset, Block, size); + mlu ->PoolUsed += size; + + mlu ->Entries[mlu ->UsedEntries].StrW = Offset; + mlu ->Entries[mlu ->UsedEntries].Len = size; + mlu ->Entries[mlu ->UsedEntries].Country = CountryCode; + mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode; + mlu ->UsedEntries++; + + return TRUE; +} + +// Convert from a 3-char code to a cmsUInt16Number. It is done in this way because some +// compilers don't properly align beginning of strings +static +cmsUInt16Number strTo16(const char str[3]) +{ + const cmsUInt8Number* ptr8; + cmsUInt16Number n; + + // For non-existent strings + if (str == NULL) return 0; + ptr8 = (const cmsUInt8Number*)str; + n = (cmsUInt16Number)(((cmsUInt16Number)ptr8[0] << 8) | ptr8[1]); + + return n; +} + +static +void strFrom16(char str[3], cmsUInt16Number n) +{ + str[0] = (char)(n >> 8); + str[1] = (char)n; + str[2] = (char)0; + +} + +// Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61) +// In the case the user explicitely sets an empty string, we force a \0 +cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString) +{ + cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString); + wchar_t* WStr; + cmsBool rc; + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + + if (mlu == NULL) return FALSE; + + // len == 0 would prevent operation, so we set a empty string pointing to zero + if (len == 0) + { + len = 1; + } + + WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len, sizeof(wchar_t)); + if (WStr == NULL) return FALSE; + + for (i=0; i < len; i++) + WStr[i] = (wchar_t) ASCIIString[i]; + + rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry); + + _cmsFree(mlu ->ContextID, WStr); + return rc; + +} + +// We don't need any wcs support library +static +cmsUInt32Number mywcslen(const wchar_t *s) +{ + const wchar_t *p; + + p = s; + while (*p) + p++; + + return (cmsUInt32Number)(p - s); +} + +// Add a wide entry. Do not add any \0 terminator (ICC1v43_2010-12.pdf page 61) +cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString) +{ + cmsUInt16Number Lang = strTo16(Language); + cmsUInt16Number Cntry = strTo16(Country); + cmsUInt32Number len; + + if (mlu == NULL) return FALSE; + if (WideString == NULL) return FALSE; + + len = (cmsUInt32Number) (mywcslen(WideString)) * sizeof(wchar_t); + if (len == 0) + len = sizeof(wchar_t); + + return AddMLUBlock(mlu, len, WideString, Lang, Cntry); +} + +// Duplicating a MLU is as easy as copying all members +cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu) +{ + cmsMLU* NewMlu = NULL; + + // Duplicating a NULL obtains a NULL + if (mlu == NULL) return NULL; + + NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries); + if (NewMlu == NULL) return NULL; + + // Should never happen + if (NewMlu ->AllocatedEntries < mlu ->UsedEntries) + goto Error; + + // Sanitize... + if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error; + + memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry)); + NewMlu ->UsedEntries = mlu ->UsedEntries; + + // The MLU may be empty + if (mlu ->PoolUsed == 0) { + NewMlu ->MemPool = NULL; + } + else { + // It is not empty + NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed); + if (NewMlu ->MemPool == NULL) goto Error; + } + + NewMlu ->PoolSize = mlu ->PoolUsed; + + if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error; + + memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed); + NewMlu ->PoolUsed = mlu ->PoolUsed; + + return NewMlu; + +Error: + + if (NewMlu != NULL) cmsMLUfree(NewMlu); + return NULL; +} + +// Free any used memory +void CMSEXPORT cmsMLUfree(cmsMLU* mlu) +{ + if (mlu) { + + if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries); + if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool); + + _cmsFree(mlu ->ContextID, mlu); + } +} + + +// The algorithm first searches for an exact match of country and language, if not found it uses +// the Language. If none is found, first entry is used instead. +static +const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu, + cmsUInt32Number *len, + cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode, + cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode) +{ + cmsUInt32Number i; + int Best = -1; + _cmsMLUentry* v; + + if (mlu == NULL) return NULL; + + if (mlu -> AllocatedEntries <= 0) return NULL; + + for (i=0; i < mlu ->UsedEntries; i++) { + + v = mlu ->Entries + i; + + if (v -> Language == LanguageCode) { + + if (Best == -1) Best = (int) i; + + if (v -> Country == CountryCode) { + + if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; + if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; + + if (len != NULL) *len = v ->Len; + + return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW); // Found exact match + } + } + } + + // No string found. Return First one + if (Best == -1) + Best = 0; + + v = mlu ->Entries + Best; + + if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; + if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; + + if (len != NULL) *len = v ->Len; + + return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW); +} + + +// Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len +cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize) +{ + const wchar_t *Wide; + cmsUInt32Number StrLen = 0; + cmsUInt32Number ASCIIlen, i; + + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + + // Sanitize + if (mlu == NULL) return 0; + + // Get WideChar + Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); + if (Wide == NULL) return 0; + + ASCIIlen = StrLen / sizeof(wchar_t); + + // Maybe we want only to know the len? + if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end + + // No buffer size means no data + if (BufferSize <= 0) return 0; + + // Some clipping may be required + if (BufferSize < ASCIIlen + 1) + ASCIIlen = BufferSize - 1; + + // Precess each character + for (i=0; i < ASCIIlen; i++) { + + if (Wide[i] == 0) + Buffer[i] = 0; + else + Buffer[i] = (char) Wide[i]; + } + + // We put a termination "\0" + Buffer[ASCIIlen] = 0; + return ASCIIlen + 1; +} + +// Obtain a wide representation of the MLU, on depending on current locale settings +cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize) +{ + const wchar_t *Wide; + cmsUInt32Number StrLen = 0; + + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + + // Sanitize + if (mlu == NULL) return 0; + + Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); + if (Wide == NULL) return 0; + + // Maybe we want only to know the len? + if (Buffer == NULL) return StrLen + sizeof(wchar_t); + + // No buffer size means no data + if (BufferSize <= 0) return 0; + + // Some clipping may be required + if (BufferSize < StrLen + sizeof(wchar_t)) + StrLen = BufferSize - + sizeof(wchar_t); + + memmove(Buffer, Wide, StrLen); + Buffer[StrLen / sizeof(wchar_t)] = 0; + + return StrLen + sizeof(wchar_t); +} + + +// Get also the language and country +CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char ObtainedLanguage[3], char ObtainedCountry[3]) +{ + const wchar_t *Wide; + + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + cmsUInt16Number ObtLang, ObtCode; + + // Sanitize + if (mlu == NULL) return FALSE; + + Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode); + if (Wide == NULL) return FALSE; + + // Get used language and code + strFrom16(ObtainedLanguage, ObtLang); + strFrom16(ObtainedCountry, ObtCode); + + return TRUE; +} + + + +// Get the number of translations in the MLU object +cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu) +{ + if (mlu == NULL) return 0; + return mlu->UsedEntries; +} + +// Get the language and country codes for a specific MLU index +cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu, + cmsUInt32Number idx, + char LanguageCode[3], + char CountryCode[3]) +{ + _cmsMLUentry *entry; + + if (mlu == NULL) return FALSE; + + if (idx >= mlu->UsedEntries) return FALSE; + + entry = &mlu->Entries[idx]; + + strFrom16(LanguageCode, entry->Language); + strFrom16(CountryCode, entry->Country); + + return TRUE; +} + + +// Named color lists -------------------------------------------------------------------------------------------- + +// Grow the list to keep at least NumElements +static +cmsBool GrowNamedColorList(cmsNAMEDCOLORLIST* v) +{ + cmsUInt32Number size; + _cmsNAMEDCOLOR * NewPtr; + + if (v == NULL) return FALSE; + + if (v ->Allocated == 0) + size = 64; // Initial guess + else + size = v ->Allocated * 2; + + // Keep a maximum color lists can grow, 100K entries seems reasonable + if (size > 1024 * 100) { + _cmsFree(v->ContextID, (void*) v->List); + v->List = NULL; + return FALSE; + } + + NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR)); + if (NewPtr == NULL) + return FALSE; + + v ->List = NewPtr; + v ->Allocated = size; + return TRUE; +} + +// Allocate a list for n elements +cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix) +{ + cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST)); + + if (v == NULL) return NULL; + + v ->List = NULL; + v ->nColors = 0; + v ->ContextID = ContextID; + + while (v -> Allocated < n) { + if (!GrowNamedColorList(v)) { + cmsFreeNamedColorList(v); + return NULL; + } + } + + strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1); + strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1); + v->Prefix[32] = v->Suffix[32] = 0; + + v -> ColorantCount = ColorantCount; + + return v; +} + +// Free a list +void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v) +{ + if (v == NULL) return; + if (v ->List) _cmsFree(v ->ContextID, v ->List); + _cmsFree(v ->ContextID, v); +} + +cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v) +{ + cmsNAMEDCOLORLIST* NewNC; + + if (v == NULL) return NULL; + + NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix); + if (NewNC == NULL) return NULL; + + // For really large tables we need this + while (NewNC ->Allocated < v ->Allocated){ + if (!GrowNamedColorList(NewNC)) + { + cmsFreeNamedColorList(NewNC); + return NULL; + } + } + + memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix)); + memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix)); + NewNC ->ColorantCount = v ->ColorantCount; + memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR)); + NewNC ->nColors = v ->nColors; + return NewNC; +} + + +// Append a color to a list. List pointer may change if reallocated +cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList, + const char* Name, + cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS]) +{ + cmsUInt32Number i; + + if (NamedColorList == NULL) return FALSE; + + if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) { + if (!GrowNamedColorList(NamedColorList)) return FALSE; + } + + for (i=0; i < NamedColorList ->ColorantCount; i++) + NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i]; + + for (i=0; i < 3; i++) + NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i]; + + if (Name != NULL) { + + strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1); + NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0; + + } + else + NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0; + + + NamedColorList ->nColors++; + return TRUE; +} + +// Returns number of elements +cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList) +{ + if (NamedColorList == NULL) return 0; + return NamedColorList ->nColors; +} + +// Info aboout a given color +cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, + char* Name, + char* Prefix, + char* Suffix, + cmsUInt16Number* PCS, + cmsUInt16Number* Colorant) +{ + if (NamedColorList == NULL) return FALSE; + + if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE; + + // strcpy instead of strncpy because many apps are using small buffers + if (Name) strcpy(Name, NamedColorList->List[nColor].Name); + if (Prefix) strcpy(Prefix, NamedColorList->Prefix); + if (Suffix) strcpy(Suffix, NamedColorList->Suffix); + if (PCS) + memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number)); + + if (Colorant) + memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant, + sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount); + + + return TRUE; +} + +// Search for a given color name (no prefix or suffix) +cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name) +{ + cmsUInt32Number i; + cmsUInt32Number n; + + if (NamedColorList == NULL) return -1; + n = cmsNamedColorCount(NamedColorList); + for (i=0; i < n; i++) { + if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0) + return (cmsInt32Number) i; + } + + return -1; +} + +// MPE support ----------------------------------------------------------------------------------------------------------------- + +static +void FreeNamedColorList(cmsStage* mpe) +{ + cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsFreeNamedColorList(List); +} + +static +void* DupNamedColorList(cmsStage* mpe) +{ + cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; + return cmsDupNamedColorList(List); +} + +static +void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); + + if (index >= NamedColorList-> nColors) { + cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index); + Out[0] = Out[1] = Out[2] = 0.0f; + } + else { + + // Named color always uses Lab + Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0); + Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0); + Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0); + } +} + +static +void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); + cmsUInt32Number j; + + if (index >= NamedColorList-> nColors) { + cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index); + for (j = 0; j < NamedColorList->ColorantCount; j++) + Out[j] = 0.0f; + + } + else { + for (j=0; j < NamedColorList ->ColorantCount; j++) + Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0); + } +} + + +// Named color lookup element +cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS) +{ + return _cmsStageAllocPlaceholder(NamedColorList ->ContextID, + cmsSigNamedColorElemType, + 1, UsePCS ? 3 : NamedColorList ->ColorantCount, + UsePCS ? EvalNamedColorPCS : EvalNamedColor, + DupNamedColorList, + FreeNamedColorList, + cmsDupNamedColorList(NamedColorList)); + +} + + +// Retrieve the named color list from a transform. Should be first element in the LUT +cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform) +{ + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + cmsStage* mpe = v ->Lut->Elements; + + if (mpe ->Type != cmsSigNamedColorElemType) return NULL; + return (cmsNAMEDCOLORLIST*) mpe ->Data; +} + + +// Profile sequence description routines ------------------------------------------------------------------------------------- + +cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n) +{ + cmsSEQ* Seq; + cmsUInt32Number i; + + if (n == 0) return NULL; + + // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked + // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door! + if (n > 255) return NULL; + + Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ)); + if (Seq == NULL) return NULL; + + Seq -> ContextID = ContextID; + Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC)); + Seq -> n = n; + + if (Seq -> seq == NULL) { + _cmsFree(ContextID, Seq); + return NULL; + } + + for (i=0; i < n; i++) { + Seq -> seq[i].Manufacturer = NULL; + Seq -> seq[i].Model = NULL; + Seq -> seq[i].Description = NULL; + } + + return Seq; +} + +void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq) +{ + cmsUInt32Number i; + + for (i=0; i < pseq ->n; i++) { + if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer); + if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model); + if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description); + } + + if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq); + _cmsFree(pseq -> ContextID, pseq); +} + +cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq) +{ + cmsSEQ *NewSeq; + cmsUInt32Number i; + + if (pseq == NULL) + return NULL; + + NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ)); + if (NewSeq == NULL) return NULL; + + + NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC)); + if (NewSeq ->seq == NULL) goto Error; + + NewSeq -> ContextID = pseq ->ContextID; + NewSeq -> n = pseq ->n; + + for (i=0; i < pseq->n; i++) { + + memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number)); + + NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg; + NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel; + memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID)); + NewSeq ->seq[i].technology = pseq ->seq[i].technology; + + NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer); + NewSeq ->seq[i].Model = cmsMLUdup(pseq ->seq[i].Model); + NewSeq ->seq[i].Description = cmsMLUdup(pseq ->seq[i].Description); + + } + + return NewSeq; + +Error: + + cmsFreeProfileSequenceDescription(NewSeq); + return NULL; +} + +// Dictionaries -------------------------------------------------------------------------------------------------------- + +// Dictionaries are just very simple linked lists + + +typedef struct _cmsDICT_struct { + cmsDICTentry* head; + cmsContext ContextID; +} _cmsDICT; + + +// Allocate an empty dictionary +cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID) +{ + _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT)); + if (dict == NULL) return NULL; + + dict ->ContextID = ContextID; + return (cmsHANDLE) dict; + +} + +// Dispose resources +void CMSEXPORT cmsDictFree(cmsHANDLE hDict) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + cmsDICTentry *entry, *next; + + _cmsAssert(dict != NULL); + + // Walk the list freeing all nodes + entry = dict ->head; + while (entry != NULL) { + + if (entry ->DisplayName != NULL) cmsMLUfree(entry ->DisplayName); + if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue); + if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name); + if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value); + + // Don't fall in the habitual trap... + next = entry ->Next; + _cmsFree(dict ->ContextID, entry); + + entry = next; + } + + _cmsFree(dict ->ContextID, dict); +} + + +// Duplicate a wide char string +static +wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr) +{ + if (ptr == NULL) return NULL; + return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t)); +} + +// Add a new entry to the linked list +cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + cmsDICTentry *entry; + + _cmsAssert(dict != NULL); + _cmsAssert(Name != NULL); + + entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry)); + if (entry == NULL) return FALSE; + + entry ->DisplayName = cmsMLUdup(DisplayName); + entry ->DisplayValue = cmsMLUdup(DisplayValue); + entry ->Name = DupWcs(dict ->ContextID, Name); + entry ->Value = DupWcs(dict ->ContextID, Value); + + entry ->Next = dict ->head; + dict ->head = entry; + + return TRUE; +} + + +// Duplicates an existing dictionary +cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict) +{ + _cmsDICT* old_dict = (_cmsDICT*) hDict; + cmsHANDLE hNew; + cmsDICTentry *entry; + + _cmsAssert(old_dict != NULL); + + hNew = cmsDictAlloc(old_dict ->ContextID); + if (hNew == NULL) return NULL; + + // Walk the list freeing all nodes + entry = old_dict ->head; + while (entry != NULL) { + + if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) { + + cmsDictFree(hNew); + return NULL; + } + + entry = entry -> Next; + } + + return hNew; +} + +// Get a pointer to the linked list +const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + + if (dict == NULL) return NULL; + return dict ->head; +} + +// Helper For external languages +const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e) +{ + if (e == NULL) return NULL; + return e ->Next; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsopt.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsopt.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsopt.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsopt.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,2005 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +//---------------------------------------------------------------------------------- + +// Optimization for 8 bits, Shaper-CLUT (3 inputs only) +typedef struct { + + cmsContext ContextID; + + const cmsInterpParams* p; // Tetrahedrical interpolation parameters. This is a not-owned pointer. + + cmsUInt16Number rx[256], ry[256], rz[256]; + cmsUInt32Number X0[256], Y0[256], Z0[256]; // Precomputed nodes and offsets for 8-bit input data + + +} Prelin8Data; + + +// Generic optimization for 16 bits Shaper-CLUT-Shaper (any inputs) +typedef struct { + + cmsContext ContextID; + + // Number of channels + cmsUInt32Number nInputs; + cmsUInt32Number nOutputs; + + _cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS]; // The maximum number of input channels is known in advance + cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS]; + + _cmsInterpFn16 EvalCLUT; // The evaluator for 3D grid + const cmsInterpParams* CLUTparams; // (not-owned pointer) + + + _cmsInterpFn16* EvalCurveOut16; // Points to an array of curve evaluators in 16 bits (not-owned pointer) + cmsInterpParams** ParamsCurveOut16; // Points to an array of references to interpolation params (not-owned pointer) + + +} Prelin16Data; + + +// Optimization for matrix-shaper in 8 bits. Numbers are operated in n.14 signed, tables are stored in 1.14 fixed + +typedef cmsInt32Number cmsS1Fixed14Number; // Note that this may hold more than 16 bits! + +#define DOUBLE_TO_1FIXED14(x) ((cmsS1Fixed14Number) floor((x) * 16384.0 + 0.5)) + +typedef struct { + + cmsContext ContextID; + + cmsS1Fixed14Number Shaper1R[256]; // from 0..255 to 1.14 (0.0...1.0) + cmsS1Fixed14Number Shaper1G[256]; + cmsS1Fixed14Number Shaper1B[256]; + + cmsS1Fixed14Number Mat[3][3]; // n.14 to n.14 (needs a saturation after that) + cmsS1Fixed14Number Off[3]; + + cmsUInt16Number Shaper2R[16385]; // 1.14 to 0..255 + cmsUInt16Number Shaper2G[16385]; + cmsUInt16Number Shaper2B[16385]; + +} MatShaper8Data; + +// Curves, optimization is shared between 8 and 16 bits +typedef struct { + + cmsContext ContextID; + + cmsUInt32Number nCurves; // Number of curves + cmsUInt32Number nElements; // Elements in curves + cmsUInt16Number** Curves; // Points to a dynamically allocated array + +} Curves16Data; + + +// Simple optimizations ---------------------------------------------------------------------------------------------------------- + + +// Remove an element in linked chain +static +void _RemoveElement(cmsStage** head) +{ + cmsStage* mpe = *head; + cmsStage* next = mpe ->Next; + *head = next; + cmsStageFree(mpe); +} + +// Remove all identities in chain. Note that pt actually is a double pointer to the element that holds the pointer. +static +cmsBool _Remove1Op(cmsPipeline* Lut, cmsStageSignature UnaryOp) +{ + cmsStage** pt = &Lut ->Elements; + cmsBool AnyOpt = FALSE; + + while (*pt != NULL) { + + if ((*pt) ->Implements == UnaryOp) { + _RemoveElement(pt); + AnyOpt = TRUE; + } + else + pt = &((*pt) -> Next); + } + + return AnyOpt; +} + +// Same, but only if two adjacent elements are found +static +cmsBool _Remove2Op(cmsPipeline* Lut, cmsStageSignature Op1, cmsStageSignature Op2) +{ + cmsStage** pt1; + cmsStage** pt2; + cmsBool AnyOpt = FALSE; + + pt1 = &Lut ->Elements; + if (*pt1 == NULL) return AnyOpt; + + while (*pt1 != NULL) { + + pt2 = &((*pt1) -> Next); + if (*pt2 == NULL) return AnyOpt; + + if ((*pt1) ->Implements == Op1 && (*pt2) ->Implements == Op2) { + _RemoveElement(pt2); + _RemoveElement(pt1); + AnyOpt = TRUE; + } + else + pt1 = &((*pt1) -> Next); + } + + return AnyOpt; +} + + +static +cmsBool CloseEnoughFloat(cmsFloat64Number a, cmsFloat64Number b) +{ + return fabs(b - a) < 0.00001f; +} + +static +cmsBool isFloatMatrixIdentity(const cmsMAT3* a) +{ + cmsMAT3 Identity; + int i, j; + + _cmsMAT3identity(&Identity); + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + if (!CloseEnoughFloat(a->v[i].n[j], Identity.v[i].n[j])) return FALSE; + + return TRUE; +} +// if two adjacent matrices are found, multiply them. +static +cmsBool _MultiplyMatrix(cmsPipeline* Lut) +{ + cmsStage** pt1; + cmsStage** pt2; + cmsStage* chain; + cmsBool AnyOpt = FALSE; + + pt1 = &Lut->Elements; + if (*pt1 == NULL) return AnyOpt; + + while (*pt1 != NULL) { + + pt2 = &((*pt1)->Next); + if (*pt2 == NULL) return AnyOpt; + + if ((*pt1)->Implements == cmsSigMatrixElemType && (*pt2)->Implements == cmsSigMatrixElemType) { + + // Get both matrices + _cmsStageMatrixData* m1 = (_cmsStageMatrixData*) cmsStageData(*pt1); + _cmsStageMatrixData* m2 = (_cmsStageMatrixData*) cmsStageData(*pt2); + cmsMAT3 res; + + // Input offset and output offset should be zero to use this optimization + if (m1->Offset != NULL || m2 ->Offset != NULL || + cmsStageInputChannels(*pt1) != 3 || cmsStageOutputChannels(*pt1) != 3 || + cmsStageInputChannels(*pt2) != 3 || cmsStageOutputChannels(*pt2) != 3) + return FALSE; + + // Multiply both matrices to get the result + _cmsMAT3per(&res, (cmsMAT3*)m2->Double, (cmsMAT3*)m1->Double); + + // Get the next in chain after the matrices + chain = (*pt2)->Next; + + // Remove both matrices + _RemoveElement(pt2); + _RemoveElement(pt1); + + // Now what if the result is a plain identity? + if (!isFloatMatrixIdentity(&res)) { + + // We can not get rid of full matrix + cmsStage* Multmat = cmsStageAllocMatrix(Lut->ContextID, 3, 3, (const cmsFloat64Number*) &res, NULL); + if (Multmat == NULL) return FALSE; // Should never happen + + // Recover the chain + Multmat->Next = chain; + *pt1 = Multmat; + } + + AnyOpt = TRUE; + } + else + pt1 = &((*pt1)->Next); + } + + return AnyOpt; +} + + +// Preoptimize just gets rif of no-ops coming paired. Conversion from v2 to v4 followed +// by a v4 to v2 and vice-versa. The elements are then discarded. +static +cmsBool PreOptimize(cmsPipeline* Lut) +{ + cmsBool AnyOpt = FALSE, Opt; + + do { + + Opt = FALSE; + + // Remove all identities + Opt |= _Remove1Op(Lut, cmsSigIdentityElemType); + + // Remove XYZ2Lab followed by Lab2XYZ + Opt |= _Remove2Op(Lut, cmsSigXYZ2LabElemType, cmsSigLab2XYZElemType); + + // Remove Lab2XYZ followed by XYZ2Lab + Opt |= _Remove2Op(Lut, cmsSigLab2XYZElemType, cmsSigXYZ2LabElemType); + + // Remove V4 to V2 followed by V2 to V4 + Opt |= _Remove2Op(Lut, cmsSigLabV4toV2, cmsSigLabV2toV4); + + // Remove V2 to V4 followed by V4 to V2 + Opt |= _Remove2Op(Lut, cmsSigLabV2toV4, cmsSigLabV4toV2); + + // Remove float pcs Lab conversions + Opt |= _Remove2Op(Lut, cmsSigLab2FloatPCS, cmsSigFloatPCS2Lab); + + // Remove float pcs Lab conversions + Opt |= _Remove2Op(Lut, cmsSigXYZ2FloatPCS, cmsSigFloatPCS2XYZ); + + // Simplify matrix. + Opt |= _MultiplyMatrix(Lut); + + if (Opt) AnyOpt = TRUE; + + } while (Opt); + + return AnyOpt; +} + +static +void Eval16nop1D(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const struct _cms_interp_struc* p) +{ + Output[0] = Input[0]; + + cmsUNUSED_PARAMETER(p); +} + +static +void PrelinEval16(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const void* D) +{ + Prelin16Data* p16 = (Prelin16Data*) D; + cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS]; + cmsUInt16Number StageDEF[cmsMAXCHANNELS]; + cmsUInt32Number i; + + for (i=0; i < p16 ->nInputs; i++) { + + p16 ->EvalCurveIn16[i](&Input[i], &StageABC[i], p16 ->ParamsCurveIn16[i]); + } + + p16 ->EvalCLUT(StageABC, StageDEF, p16 ->CLUTparams); + + for (i=0; i < p16 ->nOutputs; i++) { + + p16 ->EvalCurveOut16[i](&StageDEF[i], &Output[i], p16 ->ParamsCurveOut16[i]); + } +} + + +static +void PrelinOpt16free(cmsContext ContextID, void* ptr) +{ + Prelin16Data* p16 = (Prelin16Data*) ptr; + + _cmsFree(ContextID, p16 ->EvalCurveOut16); + _cmsFree(ContextID, p16 ->ParamsCurveOut16); + + _cmsFree(ContextID, p16); +} + +static +void* Prelin16dup(cmsContext ContextID, const void* ptr) +{ + Prelin16Data* p16 = (Prelin16Data*) ptr; + Prelin16Data* Duped = (Prelin16Data*) _cmsDupMem(ContextID, p16, sizeof(Prelin16Data)); + + if (Duped == NULL) return NULL; + + Duped->EvalCurveOut16 = (_cmsInterpFn16*) _cmsDupMem(ContextID, p16->EvalCurveOut16, p16->nOutputs * sizeof(_cmsInterpFn16)); + Duped->ParamsCurveOut16 = (cmsInterpParams**)_cmsDupMem(ContextID, p16->ParamsCurveOut16, p16->nOutputs * sizeof(cmsInterpParams*)); + + return Duped; +} + + +static +Prelin16Data* PrelinOpt16alloc(cmsContext ContextID, + const cmsInterpParams* ColorMap, + cmsUInt32Number nInputs, cmsToneCurve** In, + cmsUInt32Number nOutputs, cmsToneCurve** Out ) +{ + cmsUInt32Number i; + Prelin16Data* p16 = (Prelin16Data*)_cmsMallocZero(ContextID, sizeof(Prelin16Data)); + if (p16 == NULL) return NULL; + + p16 ->nInputs = nInputs; + p16 ->nOutputs = nOutputs; + + + for (i=0; i < nInputs; i++) { + + if (In == NULL) { + p16 -> ParamsCurveIn16[i] = NULL; + p16 -> EvalCurveIn16[i] = Eval16nop1D; + + } + else { + p16 -> ParamsCurveIn16[i] = In[i] ->InterpParams; + p16 -> EvalCurveIn16[i] = p16 ->ParamsCurveIn16[i]->Interpolation.Lerp16; + } + } + + p16 ->CLUTparams = ColorMap; + p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16; + + + p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16)); + if (p16->EvalCurveOut16 == NULL) + { + _cmsFree(ContextID, p16); + return NULL; + } + + p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* )); + if (p16->ParamsCurveOut16 == NULL) + { + + _cmsFree(ContextID, p16->EvalCurveOut16); + _cmsFree(ContextID, p16); + return NULL; + } + + for (i=0; i < nOutputs; i++) { + + if (Out == NULL) { + p16 ->ParamsCurveOut16[i] = NULL; + p16 -> EvalCurveOut16[i] = Eval16nop1D; + } + else { + + p16 ->ParamsCurveOut16[i] = Out[i] ->InterpParams; + p16 -> EvalCurveOut16[i] = p16 ->ParamsCurveOut16[i]->Interpolation.Lerp16; + } + } + + return p16; +} + + + +// Resampling --------------------------------------------------------------------------------- + +#define PRELINEARIZATION_POINTS 4096 + +// Sampler implemented by another LUT. This is a clean way to precalculate the devicelink 3D CLUT for +// almost any transform. We use floating point precision and then convert from floating point to 16 bits. +static +cmsInt32Number XFormSampler16(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER void* Cargo) +{ + cmsPipeline* Lut = (cmsPipeline*) Cargo; + cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; + cmsUInt32Number i; + + _cmsAssert(Lut -> InputChannels < cmsMAXCHANNELS); + _cmsAssert(Lut -> OutputChannels < cmsMAXCHANNELS); + + // From 16 bit to floating point + for (i=0; i < Lut ->InputChannels; i++) + InFloat[i] = (cmsFloat32Number) (In[i] / 65535.0); + + // Evaluate in floating point + cmsPipelineEvalFloat(InFloat, OutFloat, Lut); + + // Back to 16 bits representation + for (i=0; i < Lut ->OutputChannels; i++) + Out[i] = _cmsQuickSaturateWord(OutFloat[i] * 65535.0); + + // Always succeed + return TRUE; +} + +// Try to see if the curves of a given MPE are linear +static +cmsBool AllCurvesAreLinear(cmsStage* mpe) +{ + cmsToneCurve** Curves; + cmsUInt32Number i, n; + + Curves = _cmsStageGetPtrToCurveSet(mpe); + if (Curves == NULL) return FALSE; + + n = cmsStageOutputChannels(mpe); + + for (i=0; i < n; i++) { + if (!cmsIsToneCurveLinear(Curves[i])) return FALSE; + } + + return TRUE; +} + +// This function replaces a specific node placed in "At" by the "Value" numbers. Its purpose +// is to fix scum dot on broken profiles/transforms. Works on 1, 3 and 4 channels +static +cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[], + cmsUInt32Number nChannelsOut, cmsUInt32Number nChannelsIn) +{ + _cmsStageCLutData* Grid = (_cmsStageCLutData*) CLUT ->Data; + cmsInterpParams* p16 = Grid ->Params; + cmsFloat64Number px, py, pz, pw; + int x0, y0, z0, w0; + int i, index; + + if (CLUT -> Type != cmsSigCLutElemType) { + cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) Attempt to PatchLUT on non-lut stage"); + return FALSE; + } + + if (nChannelsIn == 4) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; + pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; + pw = ((cmsFloat64Number) At[3] * (p16->Domain[3])) / 65535.0; + + x0 = (int) floor(px); + y0 = (int) floor(py); + z0 = (int) floor(pz); + w0 = (int) floor(pw); + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0) || + ((pw - w0) != 0)) return FALSE; // Not on exact node + + index = (int) p16 -> opta[3] * x0 + + (int) p16 -> opta[2] * y0 + + (int) p16 -> opta[1] * z0 + + (int) p16 -> opta[0] * w0; + } + else + if (nChannelsIn == 3) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; + pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; + + x0 = (int) floor(px); + y0 = (int) floor(py); + z0 = (int) floor(pz); + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0)) return FALSE; // Not on exact node + + index = (int) p16 -> opta[2] * x0 + + (int) p16 -> opta[1] * y0 + + (int) p16 -> opta[0] * z0; + } + else + if (nChannelsIn == 1) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + + x0 = (int) floor(px); + + if (((px - x0) != 0)) return FALSE; // Not on exact node + + index = (int) p16 -> opta[0] * x0; + } + else { + cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); + return FALSE; + } + + for (i = 0; i < (int) nChannelsOut; i++) + Grid->Tab.T[index + i] = Value[i]; + + return TRUE; +} + +// Auxiliary, to see if two values are equal or very different +static +cmsBool WhitesAreEqual(cmsUInt32Number n, cmsUInt16Number White1[], cmsUInt16Number White2[] ) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + + if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremely different that the fixup should be avoided + if (White1[i] != White2[i]) return FALSE; + } + return TRUE; +} + + +// Locate the node for the white point and fix it to pure white in order to avoid scum dot. +static +cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColorSpace, cmsColorSpaceSignature ExitColorSpace) +{ + cmsUInt16Number *WhitePointIn, *WhitePointOut; + cmsUInt16Number WhiteIn[cmsMAXCHANNELS], WhiteOut[cmsMAXCHANNELS], ObtainedOut[cmsMAXCHANNELS]; + cmsUInt32Number i, nOuts, nIns; + cmsStage *PreLin = NULL, *CLUT = NULL, *PostLin = NULL; + + if (!_cmsEndPointsBySpace(EntryColorSpace, + &WhitePointIn, NULL, &nIns)) return FALSE; + + if (!_cmsEndPointsBySpace(ExitColorSpace, + &WhitePointOut, NULL, &nOuts)) return FALSE; + + // It needs to be fixed? + if (Lut ->InputChannels != nIns) return FALSE; + if (Lut ->OutputChannels != nOuts) return FALSE; + + cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut); + + if (WhitesAreEqual(nOuts, WhitePointOut, ObtainedOut)) return TRUE; // whites already match + + // Check if the LUT comes as Prelin, CLUT or Postlin. We allow all combinations + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &PreLin, &CLUT, &PostLin)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCurveSetElemType, cmsSigCLutElemType, &PreLin, &CLUT)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCLutElemType, cmsSigCurveSetElemType, &CLUT, &PostLin)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCLutElemType, &CLUT)) + return FALSE; + + // We need to interpolate white points of both, pre and post curves + if (PreLin) { + + cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PreLin); + + for (i=0; i < nIns; i++) { + WhiteIn[i] = cmsEvalToneCurve16(Curves[i], WhitePointIn[i]); + } + } + else { + for (i=0; i < nIns; i++) + WhiteIn[i] = WhitePointIn[i]; + } + + // If any post-linearization, we need to find how is represented white before the curve, do + // a reverse interpolation in this case. + if (PostLin) { + + cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PostLin); + + for (i=0; i < nOuts; i++) { + + cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]); + if (InversePostLin == NULL) { + WhiteOut[i] = WhitePointOut[i]; + + } else { + + WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]); + cmsFreeToneCurve(InversePostLin); + } + } + } + else { + for (i=0; i < nOuts; i++) + WhiteOut[i] = WhitePointOut[i]; + } + + // Ok, proceed with patching. May fail and we don't care if it fails + PatchLUT(CLUT, WhiteIn, WhiteOut, nOuts, nIns); + + return TRUE; +} + +// ----------------------------------------------------------------------------------------------------------------------------------------------- +// This function creates simple LUT from complex ones. The generated LUT has an optional set of +// prelinearization curves, a CLUT of nGridPoints and optional postlinearization tables. +// These curves have to exist in the original LUT in order to be used in the simplified output. +// Caller may also use the flags to allow this feature. +// LUTS with all curves will be simplified to a single curve. Parametric curves are lost. +// This function should be used on 16-bits LUTS only, as floating point losses precision when simplified +// ----------------------------------------------------------------------------------------------------------------------------------------------- + +static +cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsPipeline* Src = NULL; + cmsPipeline* Dest = NULL; + cmsStage* mpe; + cmsStage* CLUT; + cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL; + cmsUInt32Number nGridPoints; + cmsColorSpaceSignature ColorSpace, OutputColorSpace; + cmsStage *NewPreLin = NULL; + cmsStage *NewPostLin = NULL; + _cmsStageCLutData* DataCLUT; + cmsToneCurve** DataSetIn; + cmsToneCurve** DataSetOut; + Prelin16Data* p16; + + // This is a lossy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat)); + OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat)); + + // Color space must be specified + if (ColorSpace == (cmsColorSpaceSignature)0 || + OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE; + + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + + // For empty LUTs, 2 points are enough + if (cmsPipelineStageCount(*Lut) == 0) + nGridPoints = 2; + + Src = *Lut; + + // Named color pipelines cannot be optimized either + for (mpe = cmsPipelineGetPtrToFirstStage(Src); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (!Dest) return FALSE; + + // Prelinearization tables are kept unless indicated by flags + if (*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION) { + + // Get a pointer to the prelinearization element + cmsStage* PreLin = cmsPipelineGetPtrToFirstStage(Src); + + // Check if suitable + if (PreLin && PreLin ->Type == cmsSigCurveSetElemType) { + + // Maybe this is a linear tram, so we can avoid the whole stuff + if (!AllCurvesAreLinear(PreLin)) { + + // All seems ok, proceed. + NewPreLin = cmsStageDup(PreLin); + if(!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin)) + goto Error; + + // Remove prelinearization. Since we have duplicated the curve + // in destination LUT, the sampling should be applied after this stage. + cmsPipelineUnlinkStage(Src, cmsAT_BEGIN, &KeepPreLin); + } + } + } + + // Allocate the CLUT + CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL); + if (CLUT == NULL) goto Error; + + // Add the CLUT to the destination LUT + if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) { + goto Error; + } + + // Postlinearization tables are kept unless indicated by flags + if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) { + + // Get a pointer to the postlinearization if present + cmsStage* PostLin = cmsPipelineGetPtrToLastStage(Src); + + // Check if suitable + if (PostLin && cmsStageType(PostLin) == cmsSigCurveSetElemType) { + + // Maybe this is a linear tram, so we can avoid the whole stuff + if (!AllCurvesAreLinear(PostLin)) { + + // All seems ok, proceed. + NewPostLin = cmsStageDup(PostLin); + if (!cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin)) + goto Error; + + // In destination LUT, the sampling should be applied after this stage. + cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin); + } + } + } + + // Now its time to do the sampling. We have to ignore pre/post linearization + // The source LUT without pre/post curves is passed as parameter. + if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) { +Error: + // Ops, something went wrong, Restore stages + if (KeepPreLin != NULL) { + if (!cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin)) { + _cmsAssert(0); // This never happens + } + } + if (KeepPostLin != NULL) { + if (!cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin)) { + _cmsAssert(0); // This never happens + } + } + cmsPipelineFree(Dest); + return FALSE; + } + + // Done. + + if (KeepPreLin != NULL) cmsStageFree(KeepPreLin); + if (KeepPostLin != NULL) cmsStageFree(KeepPostLin); + cmsPipelineFree(Src); + + DataCLUT = (_cmsStageCLutData*) CLUT ->Data; + + if (NewPreLin == NULL) DataSetIn = NULL; + else DataSetIn = ((_cmsStageToneCurvesData*) NewPreLin ->Data) ->TheCurves; + + if (NewPostLin == NULL) DataSetOut = NULL; + else DataSetOut = ((_cmsStageToneCurvesData*) NewPostLin ->Data) ->TheCurves; + + + if (DataSetIn == NULL && DataSetOut == NULL) { + + _cmsPipelineSetOptimizationParameters(Dest, (_cmsPipelineEval16Fn) DataCLUT->Params->Interpolation.Lerp16, DataCLUT->Params, NULL, NULL); + } + else { + + p16 = PrelinOpt16alloc(Dest ->ContextID, + DataCLUT ->Params, + Dest ->InputChannels, + DataSetIn, + Dest ->OutputChannels, + DataSetOut); + + _cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); + } + + + // Don't fix white on absolute colorimetric + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; + + if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { + + FixWhiteMisalignment(Dest, ColorSpace, OutputColorSpace); + } + + *Lut = Dest; + return TRUE; + + cmsUNUSED_PARAMETER(Intent); +} + + +// ----------------------------------------------------------------------------------------------------------------------------------------------- +// Fixes the gamma balancing of transform. This is described in my paper "Prelinearization Stages on +// Color-Management Application-Specific Integrated Circuits (ASICs)" presented at NIP24. It only works +// for RGB transforms. See the paper for more details +// ----------------------------------------------------------------------------------------------------------------------------------------------- + + +// Normalize endpoints by slope limiting max and min. This assures endpoints as well. +// Descending curves are handled as well. +static +void SlopeLimiting(cmsToneCurve* g) +{ + int BeginVal, EndVal; + int AtBegin = (int) floor((cmsFloat64Number) g ->nEntries * 0.02 + 0.5); // Cutoff at 2% + int AtEnd = (int) g ->nEntries - AtBegin - 1; // And 98% + cmsFloat64Number Val, Slope, beta; + int i; + + if (cmsIsToneCurveDescending(g)) { + BeginVal = 0xffff; EndVal = 0; + } + else { + BeginVal = 0; EndVal = 0xffff; + } + + // Compute slope and offset for begin of curve + Val = g ->Table16[AtBegin]; + Slope = (Val - BeginVal) / AtBegin; + beta = Val - Slope * AtBegin; + + for (i=0; i < AtBegin; i++) + g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); + + // Compute slope and offset for the end + Val = g ->Table16[AtEnd]; + Slope = (EndVal - Val) / AtBegin; // AtBegin holds the X interval, which is same in both cases + beta = Val - Slope * AtEnd; + + for (i = AtEnd; i < (int) g ->nEntries; i++) + g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); +} + + +// Precomputes tables for 8-bit on input devicelink. +static +Prelin8Data* PrelinOpt8alloc(cmsContext ContextID, const cmsInterpParams* p, cmsToneCurve* G[3]) +{ + int i; + cmsUInt16Number Input[3]; + cmsS15Fixed16Number v1, v2, v3; + Prelin8Data* p8; + + p8 = (Prelin8Data*)_cmsMallocZero(ContextID, sizeof(Prelin8Data)); + if (p8 == NULL) return NULL; + + // Since this only works for 8 bit input, values comes always as x * 257, + // we can safely take msb byte (x << 8 + x) + + for (i=0; i < 256; i++) { + + if (G != NULL) { + + // Get 16-bit representation + Input[0] = cmsEvalToneCurve16(G[0], FROM_8_TO_16(i)); + Input[1] = cmsEvalToneCurve16(G[1], FROM_8_TO_16(i)); + Input[2] = cmsEvalToneCurve16(G[2], FROM_8_TO_16(i)); + } + else { + Input[0] = FROM_8_TO_16(i); + Input[1] = FROM_8_TO_16(i); + Input[2] = FROM_8_TO_16(i); + } + + + // Move to 0..1.0 in fixed domain + v1 = _cmsToFixedDomain((int) (Input[0] * p -> Domain[0])); + v2 = _cmsToFixedDomain((int) (Input[1] * p -> Domain[1])); + v3 = _cmsToFixedDomain((int) (Input[2] * p -> Domain[2])); + + // Store the precalculated table of nodes + p8 ->X0[i] = (p->opta[2] * FIXED_TO_INT(v1)); + p8 ->Y0[i] = (p->opta[1] * FIXED_TO_INT(v2)); + p8 ->Z0[i] = (p->opta[0] * FIXED_TO_INT(v3)); + + // Store the precalculated table of offsets + p8 ->rx[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v1); + p8 ->ry[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v2); + p8 ->rz[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v3); + } + + p8 ->ContextID = ContextID; + p8 ->p = p; + + return p8; +} + +static +void Prelin8free(cmsContext ContextID, void* ptr) +{ + _cmsFree(ContextID, ptr); +} + +static +void* Prelin8dup(cmsContext ContextID, const void* ptr) +{ + return _cmsDupMem(ContextID, ptr, sizeof(Prelin8Data)); +} + + + +// A optimized interpolation for 8-bit input. +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static CMS_NO_SANITIZE +void PrelinEval8(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const void* D) +{ + + cmsUInt8Number r, g, b; + cmsS15Fixed16Number rx, ry, rz; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + int OutChan; + CMSREGISTER cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + Prelin8Data* p8 = (Prelin8Data*) D; + CMSREGISTER const cmsInterpParams* p = p8 ->p; + int TotalOut = (int) p -> nOutputs; + const cmsUInt16Number* LutTable = (const cmsUInt16Number*) p->Table; + + r = (cmsUInt8Number) (Input[0] >> 8); + g = (cmsUInt8Number) (Input[1] >> 8); + b = (cmsUInt8Number) (Input[2] >> 8); + + X0 = (cmsS15Fixed16Number) p8->X0[r]; + Y0 = (cmsS15Fixed16Number) p8->Y0[g]; + Z0 = (cmsS15Fixed16Number) p8->Z0[b]; + + rx = p8 ->rx[r]; + ry = p8 ->ry[g]; + rz = p8 ->rz[b]; + + X1 = X0 + (cmsS15Fixed16Number)((rx == 0) ? 0 : p ->opta[2]); + Y1 = Y0 + (cmsS15Fixed16Number)((ry == 0) ? 0 : p ->opta[1]); + Z1 = Z0 + (cmsS15Fixed16Number)((rz == 0) ? 0 : p ->opta[0]); + + + // These are the 6 Tetrahedral + for (OutChan=0; OutChan < TotalOut; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) + { + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + } + else + if (rx >= rz && rz >= ry) + { + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + } + else + if (rz >= rx && rx >= ry) + { + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + } + else + if (ry >= rx && rx >= rz) + { + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + } + else + if (ry >= rz && rz >= rx) + { + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + } + else + if (rz >= ry && ry >= rx) + { + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + Output[OutChan] = (cmsUInt16Number) (c0 + ((Rest + (Rest >> 16)) >> 16)); + + } +} + +#undef DENS + + +// Curves that contain wide empty areas are not optimizeable +static +cmsBool IsDegenerated(const cmsToneCurve* g) +{ + cmsUInt32Number i, Zeros = 0, Poles = 0; + cmsUInt32Number nEntries = g ->nEntries; + + for (i=0; i < nEntries; i++) { + + if (g ->Table16[i] == 0x0000) Zeros++; + if (g ->Table16[i] == 0xffff) Poles++; + } + + if (Zeros == 1 && Poles == 1) return FALSE; // For linear tables + if (Zeros > (nEntries / 20)) return TRUE; // Degenerated, many zeros + if (Poles > (nEntries / 20)) return TRUE; // Degenerated, many poles + + return FALSE; +} + +// -------------------------------------------------------------------------------------------------------------- +// We need xput over here + +static +cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsPipeline* OriginalLut; + cmsUInt32Number nGridPoints; + cmsToneCurve *Trans[cmsMAXCHANNELS], *TransReverse[cmsMAXCHANNELS]; + cmsUInt32Number t, i; + cmsFloat32Number v, In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS]; + cmsBool lIsSuitable, lIsLinear; + cmsPipeline* OptimizedLUT = NULL, *LutPlusCurves = NULL; + cmsStage* OptimizedCLUTmpe; + cmsColorSpaceSignature ColorSpace, OutputColorSpace; + cmsStage* OptimizedPrelinMpe; + cmsStage* mpe; + cmsToneCurve** OptimizedPrelinCurves; + _cmsStageCLutData* OptimizedPrelinCLUT; + + + // This is a lossy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + // Only on chunky RGB + if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE; + if (T_PLANAR(*InputFormat)) return FALSE; + + if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE; + if (T_PLANAR(*OutputFormat)) return FALSE; + + // On 16 bits, user has to specify the feature + if (!_cmsFormatterIs8bit(*InputFormat)) { + if (!(*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION)) return FALSE; + } + + OriginalLut = *Lut; + + // Named color pipelines cannot be optimized either + for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + + ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat)); + OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat)); + + // Color space must be specified + if (ColorSpace == (cmsColorSpaceSignature)0 || + OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE; + + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + + // Empty gamma containers + memset(Trans, 0, sizeof(Trans)); + memset(TransReverse, 0, sizeof(TransReverse)); + + // If the last stage of the original lut are curves, and those curves are + // degenerated, it is likely the transform is squeezing and clipping + // the output from previous CLUT. We cannot optimize this case + { + cmsStage* last = cmsPipelineGetPtrToLastStage(OriginalLut); + + if (last == NULL) goto Error; + if (cmsStageType(last) == cmsSigCurveSetElemType) { + + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*)cmsStageData(last); + for (i = 0; i < Data->nCurves; i++) { + if (IsDegenerated(Data->TheCurves[i])) + goto Error; + } + } + } + + for (t = 0; t < OriginalLut ->InputChannels; t++) { + Trans[t] = cmsBuildTabulatedToneCurve16(OriginalLut ->ContextID, PRELINEARIZATION_POINTS, NULL); + if (Trans[t] == NULL) goto Error; + } + + // Populate the curves + for (i=0; i < PRELINEARIZATION_POINTS; i++) { + + v = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); + + // Feed input with a gray ramp + for (t=0; t < OriginalLut ->InputChannels; t++) + In[t] = v; + + // Evaluate the gray value + cmsPipelineEvalFloat(In, Out, OriginalLut); + + // Store result in curve + for (t=0; t < OriginalLut ->InputChannels; t++) + Trans[t] ->Table16[i] = _cmsQuickSaturateWord(Out[t] * 65535.0); + } + + // Slope-limit the obtained curves + for (t = 0; t < OriginalLut ->InputChannels; t++) + SlopeLimiting(Trans[t]); + + // Check for validity + lIsSuitable = TRUE; + lIsLinear = TRUE; + for (t=0; (lIsSuitable && (t < OriginalLut ->InputChannels)); t++) { + + // Exclude if already linear + if (!cmsIsToneCurveLinear(Trans[t])) + lIsLinear = FALSE; + + // Exclude if non-monotonic + if (!cmsIsToneCurveMonotonic(Trans[t])) + lIsSuitable = FALSE; + + if (IsDegenerated(Trans[t])) + lIsSuitable = FALSE; + } + + // If it is not suitable, just quit + if (!lIsSuitable) goto Error; + + // Invert curves if possible + for (t = 0; t < OriginalLut ->InputChannels; t++) { + TransReverse[t] = cmsReverseToneCurveEx(PRELINEARIZATION_POINTS, Trans[t]); + if (TransReverse[t] == NULL) goto Error; + } + + // Now inset the reversed curves at the begin of transform + LutPlusCurves = cmsPipelineDup(OriginalLut); + if (LutPlusCurves == NULL) goto Error; + + if (!cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, TransReverse))) + goto Error; + + // Create the result LUT + OptimizedLUT = cmsPipelineAlloc(OriginalLut ->ContextID, OriginalLut ->InputChannels, OriginalLut ->OutputChannels); + if (OptimizedLUT == NULL) goto Error; + + OptimizedPrelinMpe = cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, Trans); + + // Create and insert the curves at the beginning + if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe)) + goto Error; + + // Allocate the CLUT for result + OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL); + + // Add the CLUT to the destination LUT + if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe)) + goto Error; + + // Resample the LUT + if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error; + + // Free resources + for (t = 0; t < OriginalLut ->InputChannels; t++) { + + if (Trans[t]) cmsFreeToneCurve(Trans[t]); + if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); + } + + cmsPipelineFree(LutPlusCurves); + + + OptimizedPrelinCurves = _cmsStageGetPtrToCurveSet(OptimizedPrelinMpe); + OptimizedPrelinCLUT = (_cmsStageCLutData*) OptimizedCLUTmpe ->Data; + + // Set the evaluator if 8-bit + if (_cmsFormatterIs8bit(*InputFormat)) { + + Prelin8Data* p8 = PrelinOpt8alloc(OptimizedLUT ->ContextID, + OptimizedPrelinCLUT ->Params, + OptimizedPrelinCurves); + if (p8 == NULL) return FALSE; + + _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval8, (void*) p8, Prelin8free, Prelin8dup); + + } + else + { + Prelin16Data* p16 = PrelinOpt16alloc(OptimizedLUT ->ContextID, + OptimizedPrelinCLUT ->Params, + 3, OptimizedPrelinCurves, 3, NULL); + if (p16 == NULL) return FALSE; + + _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); + + } + + // Don't fix white on absolute colorimetric + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; + + if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { + + if (!FixWhiteMisalignment(OptimizedLUT, ColorSpace, OutputColorSpace)) { + + return FALSE; + } + } + + // And return the obtained LUT + + cmsPipelineFree(OriginalLut); + *Lut = OptimizedLUT; + return TRUE; + +Error: + + for (t = 0; t < OriginalLut ->InputChannels; t++) { + + if (Trans[t]) cmsFreeToneCurve(Trans[t]); + if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); + } + + if (LutPlusCurves != NULL) cmsPipelineFree(LutPlusCurves); + if (OptimizedLUT != NULL) cmsPipelineFree(OptimizedLUT); + + return FALSE; + + cmsUNUSED_PARAMETER(Intent); + cmsUNUSED_PARAMETER(lIsLinear); +} + + +// Curves optimizer ------------------------------------------------------------------------------------------------------------------ + +static +void CurvesFree(cmsContext ContextID, void* ptr) +{ + Curves16Data* Data = (Curves16Data*) ptr; + cmsUInt32Number i; + + for (i=0; i < Data -> nCurves; i++) { + + _cmsFree(ContextID, Data ->Curves[i]); + } + + _cmsFree(ContextID, Data ->Curves); + _cmsFree(ContextID, ptr); +} + +static +void* CurvesDup(cmsContext ContextID, const void* ptr) +{ + Curves16Data* Data = (Curves16Data*)_cmsDupMem(ContextID, ptr, sizeof(Curves16Data)); + cmsUInt32Number i; + + if (Data == NULL) return NULL; + + Data->Curves = (cmsUInt16Number**) _cmsDupMem(ContextID, Data->Curves, Data->nCurves * sizeof(cmsUInt16Number*)); + + for (i=0; i < Data -> nCurves; i++) { + Data->Curves[i] = (cmsUInt16Number*) _cmsDupMem(ContextID, Data->Curves[i], Data->nElements * sizeof(cmsUInt16Number)); + } + + return (void*) Data; +} + +// Precomputes tables for 8-bit on input devicelink. +static +Curves16Data* CurvesAlloc(cmsContext ContextID, cmsUInt32Number nCurves, cmsUInt32Number nElements, cmsToneCurve** G) +{ + cmsUInt32Number i, j; + Curves16Data* c16; + + c16 = (Curves16Data*)_cmsMallocZero(ContextID, sizeof(Curves16Data)); + if (c16 == NULL) return NULL; + + c16 ->nCurves = nCurves; + c16 ->nElements = nElements; + + c16->Curves = (cmsUInt16Number**) _cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*)); + if (c16->Curves == NULL) { + _cmsFree(ContextID, c16); + return NULL; + } + + for (i=0; i < nCurves; i++) { + + c16->Curves[i] = (cmsUInt16Number*) _cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number)); + + if (c16->Curves[i] == NULL) { + + for (j=0; j < i; j++) { + _cmsFree(ContextID, c16->Curves[j]); + } + _cmsFree(ContextID, c16->Curves); + _cmsFree(ContextID, c16); + return NULL; + } + + if (nElements == 256U) { + + for (j=0; j < nElements; j++) { + + c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], FROM_8_TO_16(j)); + } + } + else { + + for (j=0; j < nElements; j++) { + c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], (cmsUInt16Number) j); + } + } + } + + return c16; +} + +static +void FastEvaluateCurves8(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER const void* D) +{ + Curves16Data* Data = (Curves16Data*) D; + int x; + cmsUInt32Number i; + + for (i=0; i < Data ->nCurves; i++) { + + x = (In[i] >> 8); + Out[i] = Data -> Curves[i][x]; + } +} + + +static +void FastEvaluateCurves16(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER const void* D) +{ + Curves16Data* Data = (Curves16Data*) D; + cmsUInt32Number i; + + for (i=0; i < Data ->nCurves; i++) { + Out[i] = Data -> Curves[i][In[i]]; + } +} + + +static +void FastIdentity16(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER const void* D) +{ + cmsPipeline* Lut = (cmsPipeline*) D; + cmsUInt32Number i; + + for (i=0; i < Lut ->InputChannels; i++) { + Out[i] = In[i]; + } +} + + +// If the target LUT holds only curves, the optimization procedure is to join all those +// curves together. That only works on curves and does not work on matrices. +static +cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsToneCurve** GammaTables = NULL; + cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; + cmsUInt32Number i, j; + cmsPipeline* Src = *Lut; + cmsPipeline* Dest = NULL; + cmsStage* mpe; + cmsStage* ObtainedCurves = NULL; + + + // This is a lossy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + // Only curves in this LUT? + for (mpe = cmsPipelineGetPtrToFirstStage(Src); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE; + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (Dest == NULL) return FALSE; + + // Create target curves + GammaTables = (cmsToneCurve**) _cmsCalloc(Src ->ContextID, Src ->InputChannels, sizeof(cmsToneCurve*)); + if (GammaTables == NULL) goto Error; + + for (i=0; i < Src ->InputChannels; i++) { + GammaTables[i] = cmsBuildTabulatedToneCurve16(Src ->ContextID, PRELINEARIZATION_POINTS, NULL); + if (GammaTables[i] == NULL) goto Error; + } + + // Compute 16 bit result by using floating point + for (i=0; i < PRELINEARIZATION_POINTS; i++) { + + for (j=0; j < Src ->InputChannels; j++) + InFloat[j] = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); + + cmsPipelineEvalFloat(InFloat, OutFloat, Src); + + for (j=0; j < Src ->InputChannels; j++) + GammaTables[j] -> Table16[i] = _cmsQuickSaturateWord(OutFloat[j] * 65535.0); + } + + ObtainedCurves = cmsStageAllocToneCurves(Src ->ContextID, Src ->InputChannels, GammaTables); + if (ObtainedCurves == NULL) goto Error; + + for (i=0; i < Src ->InputChannels; i++) { + cmsFreeToneCurve(GammaTables[i]); + GammaTables[i] = NULL; + } + + if (GammaTables != NULL) { + _cmsFree(Src->ContextID, GammaTables); + GammaTables = NULL; + } + + // Maybe the curves are linear at the end + if (!AllCurvesAreLinear(ObtainedCurves)) { + _cmsStageToneCurvesData* Data; + + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves)) + goto Error; + Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves); + ObtainedCurves = NULL; + + // If the curves are to be applied in 8 bits, we can save memory + if (_cmsFormatterIs8bit(*InputFormat)) { + Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves); + + if (c16 == NULL) goto Error; + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup); + + } + else { + Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves); + + if (c16 == NULL) goto Error; + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup); + } + } + else { + + // LUT optimizes to nothing. Set the identity LUT + cmsStageFree(ObtainedCurves); + ObtainedCurves = NULL; + + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels))) + goto Error; + + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastIdentity16, (void*) Dest, NULL, NULL); + } + + // We are done. + cmsPipelineFree(Src); + *Lut = Dest; + return TRUE; + +Error: + + if (ObtainedCurves != NULL) cmsStageFree(ObtainedCurves); + if (GammaTables != NULL) { + for (i=0; i < Src ->InputChannels; i++) { + if (GammaTables[i] != NULL) cmsFreeToneCurve(GammaTables[i]); + } + + _cmsFree(Src ->ContextID, GammaTables); + } + + if (Dest != NULL) cmsPipelineFree(Dest); + return FALSE; + + cmsUNUSED_PARAMETER(Intent); + cmsUNUSED_PARAMETER(InputFormat); + cmsUNUSED_PARAMETER(OutputFormat); + cmsUNUSED_PARAMETER(dwFlags); +} + +// ------------------------------------------------------------------------------------------------------------------------------------- +// LUT is Shaper - Matrix - Matrix - Shaper, which is very frequent when combining two matrix-shaper profiles + + +static +void FreeMatShaper(cmsContext ContextID, void* Data) +{ + if (Data != NULL) _cmsFree(ContextID, Data); +} + +static +void* DupMatShaper(cmsContext ContextID, const void* Data) +{ + return _cmsDupMem(ContextID, Data, sizeof(MatShaper8Data)); +} + + +// A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point +// to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits, +// in total about 50K, and the performance boost is huge! +static +void MatShaperEval16(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER const void* D) +{ + MatShaper8Data* p = (MatShaper8Data*) D; + cmsS1Fixed14Number l1, l2, l3, r, g, b; + cmsUInt32Number ri, gi, bi; + + // In this case (and only in this case!) we can use this simplification since + // In[] is assured to come from a 8 bit number. (a << 8 | a) + ri = In[0] & 0xFFU; + gi = In[1] & 0xFFU; + bi = In[2] & 0xFFU; + + // Across first shaper, which also converts to 1.14 fixed point + r = p->Shaper1R[ri]; + g = p->Shaper1G[gi]; + b = p->Shaper1B[bi]; + + // Evaluate the matrix in 1.14 fixed point + l1 = (p->Mat[0][0] * r + p->Mat[0][1] * g + p->Mat[0][2] * b + p->Off[0] + 0x2000) >> 14; + l2 = (p->Mat[1][0] * r + p->Mat[1][1] * g + p->Mat[1][2] * b + p->Off[1] + 0x2000) >> 14; + l3 = (p->Mat[2][0] * r + p->Mat[2][1] * g + p->Mat[2][2] * b + p->Off[2] + 0x2000) >> 14; + + // Now we have to clip to 0..1.0 range + ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384U : (cmsUInt32Number) l1); + gi = (l2 < 0) ? 0 : ((l2 > 16384) ? 16384U : (cmsUInt32Number) l2); + bi = (l3 < 0) ? 0 : ((l3 > 16384) ? 16384U : (cmsUInt32Number) l3); + + // And across second shaper, + Out[0] = p->Shaper2R[ri]; + Out[1] = p->Shaper2G[gi]; + Out[2] = p->Shaper2B[bi]; + +} + +// This table converts from 8 bits to 1.14 after applying the curve +static +void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) +{ + int i; + cmsFloat32Number R, y; + + for (i=0; i < 256; i++) { + + R = (cmsFloat32Number) (i / 255.0); + y = cmsEvalToneCurveFloat(Curve, R); + + if (y < 131072.0) + Table[i] = DOUBLE_TO_1FIXED14(y); + else + Table[i] = 0x7fffffff; + } +} + +// This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve +static +void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) +{ + int i; + cmsFloat32Number R, Val; + + for (i=0; i < 16385; i++) { + + R = (cmsFloat32Number) (i / 16384.0); + Val = cmsEvalToneCurveFloat(Curve, R); // Val comes 0..1.0 + + if (Val < 0) + Val = 0; + + if (Val > 1.0) + Val = 1.0; + + if (Is8BitsOutput) { + + // If 8 bits output, we can optimize further by computing the / 257 part. + // first we compute the resulting byte and then we store the byte times + // 257. This quantization allows to round very quick by doing a >> 8, but + // since the low byte is always equal to msb, we can do a & 0xff and this works! + cmsUInt16Number w = _cmsQuickSaturateWord(Val * 65535.0); + cmsUInt8Number b = FROM_16_TO_8(w); + + Table[i] = FROM_8_TO_16(b); + } + else Table[i] = _cmsQuickSaturateWord(Val * 65535.0); + } +} + +// Compute the matrix-shaper structure +static +cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, cmsVEC3* Off, cmsToneCurve* Curve2[3], cmsUInt32Number* OutputFormat) +{ + MatShaper8Data* p; + int i, j; + cmsBool Is8Bits = _cmsFormatterIs8bit(*OutputFormat); + + // Allocate a big chuck of memory to store precomputed tables + p = (MatShaper8Data*) _cmsMalloc(Dest ->ContextID, sizeof(MatShaper8Data)); + if (p == NULL) return FALSE; + + p -> ContextID = Dest -> ContextID; + + // Precompute tables + FillFirstShaper(p ->Shaper1R, Curve1[0]); + FillFirstShaper(p ->Shaper1G, Curve1[1]); + FillFirstShaper(p ->Shaper1B, Curve1[2]); + + FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits); + FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits); + FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits); + + // Convert matrix to nFixed14. Note that those values may take more than 16 bits + for (i=0; i < 3; i++) { + for (j=0; j < 3; j++) { + p ->Mat[i][j] = DOUBLE_TO_1FIXED14(Mat->v[i].n[j]); + } + } + + for (i=0; i < 3; i++) { + + if (Off == NULL) { + p ->Off[i] = 0; + } + else { + p ->Off[i] = DOUBLE_TO_1FIXED14(Off->n[i]); + } + } + + // Mark as optimized for faster formatter + if (Is8Bits) + *OutputFormat |= OPTIMIZED_SH(1); + + // Fill function pointers + _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper); + return TRUE; +} + +// 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast! +static +cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsStage* Curve1, *Curve2; + cmsStage* Matrix1, *Matrix2; + cmsMAT3 res; + cmsBool IdentityMat; + cmsPipeline* Dest, *Src; + cmsFloat64Number* Offset; + + // Only works on RGB to RGB + if (T_CHANNELS(*InputFormat) != 3 || T_CHANNELS(*OutputFormat) != 3) return FALSE; + + // Only works on 8 bit input + if (!_cmsFormatterIs8bit(*InputFormat)) return FALSE; + + // Seems suitable, proceed + Src = *Lut; + + // Check for: + // + // shaper-matrix-matrix-shaper + // shaper-matrix-shaper + // + // Both of those constructs are possible (first because abs. colorimetric). + // additionally, In the first case, the input matrix offset should be zero. + + IdentityMat = FALSE; + if (cmsPipelineCheckAndRetreiveStages(Src, 4, + cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + &Curve1, &Matrix1, &Matrix2, &Curve2)) { + + // Get both matrices + _cmsStageMatrixData* Data1 = (_cmsStageMatrixData*)cmsStageData(Matrix1); + _cmsStageMatrixData* Data2 = (_cmsStageMatrixData*)cmsStageData(Matrix2); + + // Input offset should be zero + if (Data1->Offset != NULL) return FALSE; + + // Multiply both matrices to get the result + _cmsMAT3per(&res, (cmsMAT3*)Data2->Double, (cmsMAT3*)Data1->Double); + + // Only 2nd matrix has offset, or it is zero + Offset = Data2->Offset; + + // Now the result is in res + Data2 -> Offset. Maybe is a plain identity? + if (_cmsMAT3isIdentity(&res) && Offset == NULL) { + + // We can get rid of full matrix + IdentityMat = TRUE; + } + + } + else { + + if (cmsPipelineCheckAndRetreiveStages(Src, 3, + cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + &Curve1, &Matrix1, &Curve2)) { + + _cmsStageMatrixData* Data = (_cmsStageMatrixData*)cmsStageData(Matrix1); + + // Copy the matrix to our result + memcpy(&res, Data->Double, sizeof(res)); + + // Preserve the Odffset (may be NULL as a zero offset) + Offset = Data->Offset; + + if (_cmsMAT3isIdentity(&res) && Offset == NULL) { + + // We can get rid of full matrix + IdentityMat = TRUE; + } + } + else + return FALSE; // Not optimizeable this time + + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (!Dest) return FALSE; + + // Assamble the new LUT + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1))) + goto Error; + + if (!IdentityMat) { + + if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest->ContextID, 3, 3, (const cmsFloat64Number*)&res, Offset))) + goto Error; + } + + if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2))) + goto Error; + + // If identity on matrix, we can further optimize the curves, so call the join curves routine + if (IdentityMat) { + + OptimizeByJoiningCurves(&Dest, Intent, InputFormat, OutputFormat, dwFlags); + } + else { + _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1); + _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2); + + // In this particular optimization, cache does not help as it takes more time to deal with + // the cache that with the pixel handling + *dwFlags |= cmsFLAGS_NOCACHE; + + // Setup the optimizarion routines + SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Offset, mpeC2->TheCurves, OutputFormat); + } + + cmsPipelineFree(Src); + *Lut = Dest; + return TRUE; +Error: + // Leave Src unchanged + cmsPipelineFree(Dest); + return FALSE; +} + + +// ------------------------------------------------------------------------------------------------------------------------------------- +// Optimization plug-ins + +// List of optimizations +typedef struct _cmsOptimizationCollection_st { + + _cmsOPToptimizeFn OptimizePtr; + + struct _cmsOptimizationCollection_st *Next; + +} _cmsOptimizationCollection; + + +// The built-in list. We currently implement 4 types of optimizations. Joining of curves, matrix-shaper, linearization and resampling +static _cmsOptimizationCollection DefaultOptimization[] = { + + { OptimizeByJoiningCurves, &DefaultOptimization[1] }, + { OptimizeMatrixShaper, &DefaultOptimization[2] }, + { OptimizeByComputingLinearization, &DefaultOptimization[3] }, + { OptimizeByResampling, NULL } +}; + +// The linked list head +_cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginOptimizationList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsOptimizationPluginChunkType newHead = { NULL }; + _cmsOptimizationCollection* entry; + _cmsOptimizationCollection* Anterior = NULL; + _cmsOptimizationPluginChunkType* head = (_cmsOptimizationPluginChunkType*) src->chunks[OptimizationPlugin]; + + _cmsAssert(ctx != NULL); + _cmsAssert(head != NULL)