GithubHelp home page GithubHelp logo

fastffi's Introduction

fastFFI: Modern and Efficient FFI for Java and C++

Basically, fastFFI has three components:

  • FFI: DSL and API used to develop FFI applications.
  • Annotation Processor: the code generator for FFI.
  • LLVM4JNI: LLVM4JNI has two submodules:
    • LLVM4JNI: a tool that translates LLVM bitcode into Java bytecode.
    • LLVM4JNI Runtime: the runtime component used by generated bytecode.

An FFI application must include ffi and llvm4jni-runtime in its class path as runtime dependency.

Build

  1. Checkout source code

    git clone <path-to-fastffi> fastffi
  2. Prepare building environment

    export LLVM11_HOME=<path-to-llvm-11>

    LLVM11_HOME should point to the home of LLVM 11. In Ubuntu, it is at /usr/lib/llvm-11. Basically, the build procedure the following binary:

    • $LLVM11_HOME/bin/clang++
    • $LLVM11_HOME/bin/ld.lld
    • $LLVM11_HOME/lib/cmake/llvm
  3. Use fastFFI with Maven.

    <properties>
        <fastffi.revision>0.1.2</fastffi.revision>
    </properties>
    
    <dependencies>
        <!-- The FFI annotation -->
        <dependency>
            <groupId>com.alibaba.fastffi</groupId>
            <artifactId>ffi</artifactId>
            <version>${fastffi.revision}</version>
        </dependency>
        <!-- The FFI annotation processor for code generation -->
        <dependency>
            <groupId>com.alibaba.fastffi</groupId>
            <artifactId>annotation-processor</artifactId>
            <version>${fastffi.revision}</version>
        </dependency>
    
        <!-- The runtime component of LLVM4JNI -->
        <dependency>
            <groupId>com.alibaba.fastffi</groupId>
            <artifactId>llvm4jni</artifactId>
            <version>${fastffi.revision}</version>
            <classifier>${os.detected.classifier}</classifier>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastffi</groupId>
            <artifactId>llvm4jni-runtime</artifactId>
            <version>${fastffi.revision}</version>
        </dependency>
    </dependencies>
    
    <plugins>
        <plugin>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.7.0</version>
            <executions>
                <execution>
                    <phase>initialize</phase>
                    <goals>
                        <goal>detect</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
  4. Use maven to build your applications.

    The generated code, including Java and C++ code, is available in <project.dir>/target/generated-source/annotations

Options

A Java programming language compiler must support standard options in the format -Akey[=value]. fastFFI provides the following options:

  1. fastffi.handleException: whether generating code to handle C++ exceptions
    • default value: false
  2. fastffi.manualBoxing: using new Integer() or new Long() to box a primitive integer.
    • default value: true
    • Auto boxing uses Integer.valueOf or Long.valueOf, which cannot be properly handled by the escape analysis of C2 compiler.
  3. fastffi.strictTypeCheck
    • default value: false
  4. fastffi.nullReturnValueCheck
    • default value: true
    • insert additional null check for native pointers
  5. fastffi.cxxOutputLocation
    • default value: CLASS_OUTPUT
    • accept values: CLASS_OUTPUT, SOURCE_OUTPUT, NATIVE_HEADER_OUTPUT.
  6. fastffi.traceJNICalls
    • default value: false
    • generate stuffs to trace the invocations of JNI wrappers
  7. fastffi.compactFFINames
    • default value: true
    • generate compact FFI wrapper type names, non-compact names will benefit debugging, but increase the binary size

Usage:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.0</version>
    <configuration>
        <compilerVersion>${javac.target}</compilerVersion>
        <source>${javac.target}</source>
        <target>${javac.target}</target>
        <compilerArgs>
            <arg>-Afastffi.strictTypeCheck=true</arg>
        </compilerArgs>
    </configuration>
</plugin>

Build Mac OS

  1. Install a JDK (JDK 8 and 11)

  2. Install LLVM 11, Maven and CMake

    brew install llvm@11 cmake maven
    
  3. Set ENV

    export LLVM11_HOME=/usr/local/opt/llvm@11
    

FAQ

TBA

fastffi's People

Contributors

d-d-h avatar sighingnow avatar tianxiaogu avatar zhanglei1949 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fastffi's Issues

[Bug] Type alias used but not generated

C++ code

namespace vineyard {
struct BoolArr {
};

template <typename T>
struct ConvertToArrowType {};

template <>
struct ConvertToArrowType<bool> {
    using ArrayType = BoolArr;
};

class __attribute__((annotate("vineyard"))) BooleanArray {
 private:
public:
  using ArrayType = typename ConvertToArrowType<bool>::ArrayType;

  ArrayType* GetArray() const;
};
}  // namespace

Generates Java code

package vineyard;

import com.alibaba.fastffi.CXXHead;
import com.alibaba.fastffi.CXXPointer;
import com.alibaba.fastffi.FFIExpr;
import com.alibaba.fastffi.FFIGen;
import com.alibaba.fastffi.FFIPointer;
import com.alibaba.fastffi.FFITypeAlias;

@FFITypeAlias("vineyard::BooleanArray")
@FFIGen
@CXXHead("basic/ds/arrow.vineyard.h")
public interface BooleanArray extends FFIPointer {
  ArrayType GetArray();

  @FFITypeAlias("vineyard::BooleanArray::ArrayType")
  @FFIGen
  @CXXHead("basic/ds/arrow.vineyard.h")
  interface ArrayType extends CXXPointer {
    @FFIExpr("{0}")
    vineyard.ArrayType get();
  }
}

package vineyard;

import com.alibaba.fastffi.CXXHead;
import com.alibaba.fastffi.FFIGen;
import com.alibaba.fastffi.FFIPointer;
import com.alibaba.fastffi.FFITypeAlias;

@FFITypeAlias("vineyard::BoolArr")
@FFIGen
@CXXHead("basic/ds/arrow.vineyard.h")
public interface BoolArr extends FFIPointer {
}

Build vineyard.ConvertToArrowType@1349277854
package vineyard;

import com.alibaba.fastffi.CXXHead;
import com.alibaba.fastffi.FFIGen;
import com.alibaba.fastffi.FFIPointer;
import com.alibaba.fastffi.FFITypeAlias;

@FFITypeAlias("vineyard::ConvertToArrowType")
@FFIGen
@CXXHead("basic/ds/arrow.vineyard.h")
public interface ConvertToArrowType<T> extends FFIPointer {
}

Note that a vineyard.ArrayType is used, but not generated.

[BUG] Codegen failure

When I try to Generating the a template FFIPointer classArrowFragment, the ffi throws error
<<
[ERROR] /Users/xiaolei.zl/lei/work/aone/gs/analytical_engine/java/grape-runtime/target/generated-sources/annotations/com/alibaba/graphscope/fragment/ArrowFragmentGen.java:[28,17] java.lang.IllegalStateException: A TypeDef has accepted two generation requests: TypeDef{typeElementName='com.alibaba.graphscope.fragment.ArrowFragment', declaredTypeElementName='com.alibaba.graphscope.fragment.ArrowFragment', [email protected](include={}, java={"Long"}, cxxFull="", cxx={"int64_t"}), cxxFullTypeName='gs::ArrowFragmentDefault<int64_t>', packageName='com.alibaba.graphscope.fragment', simpleClassName='ArrowFragment_cxx_0x999c91c4', fullClassName='com.alibaba.graphscope.fragment.ArrowFragment_cxx_0x999c91c4', [email protected](functionTemplates={@com.alibaba.fastffi.FFIFunGen(templates={@com.alibaba.fastffi.CXXTemplate(include={}, java={"Long"}, cxxFull="", cxx={"int64_t"}), @com.alibaba.fastffi.CXXTemplate(include={}, java={"Double"}, cxxFull="", cxx={"double"}), @com.alibaba.fastffi.CXXTemplate(include={}, java={"Integer"}, cxxFull="", cxx={"int32_t"})}, name="edgeDataColumn", parameterTypes={"DATA_T"}, returnType="com.alibaba.graphscope.ds.EdgeDataColumn<DATA_T>")}, templates={@com.alibaba.fastffi.CXXTemplate(include={}, java={"Long"}, cxxFull="", cxx={"int64_t"})}, type="com.alibaba.graphscope.fragment.ArrowFragment", library=""), ffiFunGens=[@com.alibaba.fastffi.FFIFunGen(templates={@com.alibaba.fastffi.CXXTemplate(include={}, java={"Long"}, cxxFull="", cxx={"int64_t"}), @com.alibaba.fastffi.CXXTemplate(include={}, java={"Double"}, cxxFull="", cxx={"double"}), @com.alibaba.fastffi.CXXTemplate(include={}, java={"Integer"}, cxxFull="", cxx={"int32_t"})}, name="edgeDataColumn", parameterTypes={"DATA_T"}, returnType="com.alibaba.graphscope.ds.EdgeDataColumn<DATA_T>")], [email protected](value="gs::ArrowFragmentDefault"), ffiNamespace=null, ffiMirror=null, ffiLibrary=null} vs. TypeDef{typeElementName='com.alibaba.graphscope.fragment.ArrowFragment', declaredTypeElementName='com.alibaba.graphscope.fragment.ArrowFragment', template=com.alibaba.fastffi.annotation.TypeEnv$1@10, cxxFullTypeName='gs::ArrowFragmentDefault<int64_t>', packageName='com.alibaba.graphscope.fragment', simpleClassName='ArrowFragment_cxx_0x999c91c4', fullClassName='com.alibaba.graphscope.fragment.ArrowFragment_cxx_0x999c91c4', [email protected](functionTemplates={}, templates={}, type="", library="grape-jni"), ffiFunGens=[], [email protected](value="gs::ArrowFragmentDefault"), ffiNamespace=null, ffiMirror=null, ffiLibrary=null}

The arrowFragment is defined as

public interface ArrowFragment<OID_T> extends FFIPointer {
}
  • When do I meet this problem?
    After jump from commit a166c6287f2efb938c27fb01b3d499932d484f9c to release0.1

According to my test, just uncomment https://github.com/alibaba/fastFFI/blob/main/annotation-processor/src/main/java/com/alibaba/fastffi/annotation/TypeDefRegistry.java#L177 works fine for me...

Problems about usage in maven

A possible problem

I copied maven usage in README and run mvn test but met error:

Could not resolve dependencies for project com.alibaba:ffi-test:ja
r:1.0-SNAPSHOT: Failure to find com.alibaba.fastffi:llvm4jni-runtime:jar:linux-x86_64:0.1.2 in https://maven.aliyun.co
m/repository/public was cached in the local repository, resolution will not be reattempted until the update interval o
f aliyunmaven has elapsed or updates are forced -> [Help 1]

I compared it with the usages in FlatBuffers sample, and remove <classifier>${os.detected.classifier}</classifier> , than it worked.

Screenshot (112)

Should I add llvm4jni in pom.xml?

As you can see in the screenshot, the FlatBuffers sample used the llvm4jni dependency, but README didn't use that. When should I add the llvm4jni dependency?

C language encapsulation

Hello, may I ask if this library supports encapsulating the C language? For example, how can the following code be packaged using fastffi?

// func.h 
extern "C" {
extern int func(int **matrix, int n);
};

// func.cpp
int func (int ** matrix, int n) {
    // do xxx
    return sum;
}

Check C++ types with the same identifiers

We assume that all C++ types with the same identifier have the same definition among all input files.
This assumption is taken without any check. We need to carefully check this assumption if possible.

// foo.h
class C {
  int i;
};
// bar.h
class C {
 std::string s;
};

@CXXSuperTemplate doens't work if the super class is placed in another file

For class

@FFITypeAlias("arrow::Result")
@FFIGen(templates = @CXXTemplate(cxx = "int", java = "Integer"))
@CXXSuperTemplate(
        type = "arrow.EqualityComparable",
        template = @CXXTemplate(java = "arrow.Result<T>", cxx = "arrow::Result<T>")
)
@CXXHead(
        system = "arrow/result.h"
)
public interface Result<T> extends EqualityComparable<Result<T>>, CXXPointer {
}

and class

@FFITypeAlias("arrow::util::EqualityComparable")
@FFIGen
@CXXHead(
        system = "arrow/util/compare.h"
)
public interface EqualityComparable<T> extends CXXPointer
{
}

The CXXSuperTemplate doesn't work if they are placed in different java files, but works if been placed in the the file.

[BUG] Fail to check FunGen match with Function definition

For a sample FFIPointer class with generic functions.

@FFIGen(library = LIBRARY_NAME)
@CXXHead(HEADER)
@FFITypeAlias(TYPE_ALIAS)
public interface MyClass extends FFIPointer {
    @FFINameAlias("Sum")
    <MSG_T> void sum(@FFIConst @CXXReference MSG_T msgIn, @CXXReference MSG_T msgOut);
}

When using @FFIGen to invoke code generation, for example

@FFIGenBatch(
    value = {
         @FFIGen(
              type = "com.my.MyClass",
              functionTemplates = {
                  @FFIFunGen(
                      name = "sum",
                      returnType = "void",
                      parameterTypes = {"MSG_T"},
                      templates = {
                          @CXXTemplate(
                                cxx = {DOUBLE_MSG},
                                java = {"com.My.DoubleMsg"})
                              }
                    )
                 }
             )
       }
)

NO implementation code for the specified parameter types are generated.

Missing generation of some `@FFIGen` classes

We have following classes,

@FFIGen
public interface BinaryType extends CXXPointer {}

@FFIGen
public interface BaseArray<TYPE> extends CXXPointer {}

@FFIGen
public interface BinaryArray extends BaseArray<BinaryType>, CXXPointer {}

The processing order (returned by roundEnvironment.getElementsAnnotatedWith(e)) might be

[BaseArray, BinaryArray, BinaryType]

When genereting BinaryArray, the BinaryType will be processed, but won't be generated, as genIfMissing is True (see 1), but will be added to the map (see 2). When processing BinaryType, it was treated as added, and won't be generated.

[Bug] binding-generator fails to process pointer of type parameter as template argument type with `java.lang.ClassCastException`

The binding generator cannot handle cases where pointer types points to a type parameter. For following code

#pragma once

template <typename T>
class reverse_iterator {
};

template <class CharT>
class basic_string_view
{
public:
    typedef CharT const * const_pointer;
    typedef const_pointer const_iterator;

    typedef reverse_iterator< const_iterator > const_reverse_iterator;

    const_reverse_iterator begin()  const {
        return const_reverse_iterator{};
    }
};

The binding generator throws an exception:

Exception in thread "main" java.lang.ClassCastException: com.squareup.javapoet.TypeVariableName cannot be cast to com.squareup.javapoet.ClassName
	at com.alibaba.fastffi.tool.FFIBindingGenerator.getPointerOfPointer(FFIBindingGenerator.java:1736)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.lambda$getJavaTypeForTemplateSpecializationType$8(FFIBindingGenerator.java:1913)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.forEachTemplateArgument(FFIBindingGenerator.java:1168)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.getJavaTypeForTemplateSpecializationType(FFIBindingGenerator.java:1905)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.typeToTypeName(FFIBindingGenerator.java:1604)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.typeToFFIType(FFIBindingGenerator.java:1759)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.getJavaTypeForTypedefType(FFIBindingGenerator.java:1671)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.typeToTypeName(FFIBindingGenerator.java:1613)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.typeToFFIType(FFIBindingGenerator.java:1759)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.buildReturnAndParamTypes(FFIBindingGenerator.java:1532)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.generateFFIPointer(FFIBindingGenerator.java:2387)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.generateCXXRecord(FFIBindingGenerator.java:2079)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.generate(FFIBindingGenerator.java:1312)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.generateClassTemplate(FFIBindingGenerator.java:1495)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.generate(FFIBindingGenerator.java:1315)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.generate(FFIBindingGenerator.java:1282)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.generate(FFIBindingGenerator.java:1272)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.generate(FFIBindingGenerator.java:1067)
	at com.alibaba.fastffi.tool.FFIBindingGenerator.main(FFIBindingGenerator.java:423)

FFITypeAlias on dependent name type

For nested types inside a template class,

template <typename T>
class A {
    class B {
    };
};

We could have the following annotated java code,

@FFITypeAlias("A")
public interface<T> {
};

@FFITypeAlias("A::B")
public interface B<T> {
}

But that doesn't work, as interface B<T> should be A<T>::B, rather than A::B<T>.

A trick is de-nest the types using an alias,

template <typename T>
using C = typename A<T>::B;

@FFITypeAlias("C")
public interface C<T> {
}

But that requires extra work for users.

@tianxiaogu Could we change the @FFITypeAlias to accept something like "A{0}::B" to provide inject points for template type parameters?

[BUG] Build failure

The path to javadoc doesn't reside in this location

                    <configuration>
                        <javadocExecutable>${java.home}/../bin/javadoc</javadocExecutable>
                        <doclint>none</doclint>
                    </configuration>

Failed to processing types that involves `using` a dependent name type

C++ source code for reproduce:

#pragma once

#include <memory>

namespace vineyard {

template <typename T>
struct Factory {
    using type = T;
};

template <typename T>
class Array {
  public:
    using ArrayType = typename Factory<T>::type;

    std::shared_ptr<ArrayType> get();

    void set(std::shared_ptr<ArrayType>);
};

}

Name clash for overloading in subclass, due to name erase

Consider,

public interface ArrayBuilder extends CXXPointer {
  @CXXValue
  Status Finish(@FFITypeAlias("std::shared_ptr<arrow::Array>") shared_ptr<Array> out);
}

and

public interface FixedSizeBinaryBuilder extends ArrayBuilder, FFIPointer {
  @CXXValue
  Status Finish(
      @FFITypeAlias("std::shared_ptr<arrow::FixedSizeBinaryArray>") shared_ptr<FixedSizeBinaryArray> out);
}

It won't succeed, as the child class will re-generate a copy of its parent's methods, but as their arguments are both shared_ptr<...>, it will be though as the same method in the context of Java language.

[BUG] Build failure

When using ffi from maven central repo, I got error

Could not resolve dependencies for project com.alibaba.graphscope:grape-jdk:jar:0.1: Failed to collect dependencies at com.alibaba.fastffi:ffi:jar:0.1.1: Failed to read artifact descriptor for com.alibaba.fastffi:ffi:jar:0.1.1: Could not find artifact com.alibaba.fastffi:fastffi-parent:pom:${revision}

Building fails: no member named 'getUsingDecl' in 'clang::ConstructorUsingShadowDecl'

Hi,

This project seems promising and I wanted to give it a try but I get the following error when building, either with llvm 11 or 13:

[INFO]      [exec] /usr/lib/llvm/11/bin/clang++ -Dllvm4jni_EXPORTS -I/usr/lib/llvm/13/include -isystem /opt/openjdk/include -isystem /opt/openjdk/include/linux -flto -fforce-emit-vtables -std=c++14 -Wall -fPIC   -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fPIC -MD -MT CMakeFiles/llvm4jni.dir/target/generated-sources/annotations/jni_com_alibaba_fastffi_clang_DeclaratorDecl_cxx_0x7852b4ac.cc.o -MF CMakeFiles/llvm4jni.dir/target/generated-sources/annotations/jni_com_alibaba_fastffi_clang_DeclaratorDecl_cxx_0x7852b4ac.cc.o.d -o CMakeFiles/llvm4jni.dir/target/generated-sources/annotations/jni_com_alibaba_fastffi_clang_DeclaratorDecl_cxx_0x7852b4ac.cc.o -c /home/java/fastFFI/llvm/target/generated-sources/annotations/jni_com_alibaba_fastffi_clang_DeclaratorDecl_cxx_0x7852b4ac.cc
[INFO]      [exec] /home/java/fastFFI/llvm/target/generated-sources/annotations/jni_com_alibaba_fastffi_clang_ConstructorUsingShadowDecl_cxx_0xefa0f2e5.cc:98:92: error: no member named 'getUsingDecl' in 'clang::ConstructorUsingShadowDecl'
[INFO]      [exec]         return reinterpret_cast<jlong>(reinterpret_cast<clang::ConstructorUsingShadowDecl*>(ptr)->getUsingDecl());
[INFO]      [exec]                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  ^

Any idea ?

[Bug] Unsupported function is skiped sliently by the binding-generator

For following code

#pragma once

template <typename T>
class reverse_iterator {
};

template <class CharT>
class basic_string_view
{
public:
    typedef CharT const * const_pointer;
    typedef const_pointer const_iterator;

    typedef reverse_iterator< CharT > const_reverse_iterator;

    const_reverse_iterator begin()  const {
        return const_reverse_iterator{};
    }
};

The binding-generator generates

Build fastffi.reverse_iterator@762218386
package fastffi;

import com.alibaba.fastffi.CXXHead;
import com.alibaba.fastffi.FFIGen;
import com.alibaba.fastffi.FFIPointer;
import com.alibaba.fastffi.FFITypeAlias;

@FFITypeAlias("reverse_iterator")
@FFIGen
@CXXHead("basic/ds/stringview.vineyard-mod")
public interface reverse_iterator<T> extends FFIPointer {
}

Build fastffi.basic_string_view@1551870003
package fastffi;

import com.alibaba.fastffi.CXXHead;
import com.alibaba.fastffi.FFIGen;
import com.alibaba.fastffi.FFIPointer;
import com.alibaba.fastffi.FFITypeAlias;

@FFITypeAlias("basic_string_view")
@FFIGen
@CXXHead("basic/ds/stringview.vineyard-mod")
public interface basic_string_view<CharT> extends FFIPointer {
}

Note that the begin() method is skiped and it would occur if we change the return type of begin() to int.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.