html<!DOCTYPE html>
<html>
    <head>
    <title>Hello Rust + wasm + WebGL!</title>
    </head>
    <body>
    <canvas id="canvas" width="320" height="240"></canvas>
    <script src="./index.js" type="module"></script>
    </body>
</html>
html<!DOCTYPE html>
<html>
    <head>
    <title>Hello Rust + wasm + WebGL!</title>
    </head>
    <body>
    <canvas id="canvas" width="320" height="240"></canvas>
    <script src="./index.js" type="module"></script>
    </body>
</html>
JavaScript// Uint8Array(UTF8) ⇒ String(UTF16)
export function string_from_utf8(u8_ary)
{
    let str = "";
    let ptr8 = 0;
    let len8 = u8_ary.length;

    // Stringの1文字追加効率悪い? とりあえずキャッシュ
    let str_cache = new Uint16Array(256);
    let cptr = 0;
    do {
        let utf32 = 0;
        do {//utf8 > utf32
            // 0xxx xxxx
            let c0 = u8_ary[ptr8++];
            if( c0 < 0x80 ){utf32 = c0; break;}

            // 110yyyyx
            if( (c0 & 0xe0) == 0xc0 ){ //len = 2
                if( ptr8+1 > len8)break;
                if( (c0 & 0x1e) == 0 )break;// yyyyどれか必ず1 
                let c1 = u8_ary[ptr8++];
                if( (c1 & 0xc0) != 0x80 )break;//10ではない
                utf32 = ((c0 & 0x1f)<<6) | (c1 & 0x3f);
                break;
            }

            // 1110yyyy 10yxxxxx 10xxxxxxx
            // yyyy yxxx xxxx xxxx
            if( (c0 & 0xf0) == 0xe0 ){ //len = 3
                if( ptr8+2 > len8)break;
                let v = (c0 & 0x0f) << 12;
                let c1 = u8_ary[ptr8++];
                if( (c1 & 0xc0) != 0x80 )break;//10ではない
                v |= (c1 & 0x3f) << 6;
                let c2 = u8_ary[ptr8++];
                if( (c2 & 0xc0) != 0x80 )break;//10ではない
                v |= (c2 & 0x3f);
                if( (v & 0xf800) == 0 )break;//yyyyyどれか必ず1
                utf32 = v;
                break;
            }
            // 1111 0yyy 10yy xxxx 10xxx xxx 10xx xxxx
            // y yyyy xxxx xxxx xxxx xxxx
            if ( (c0 & 0xf8) == 0xf0 ) { //len = 4
                if( ptr8+3 > len8)break;
                let v = (c0 & 0x07) << 18;
                let c1 = u8_ary[ptr8++];
                if( (c1 & 0xc0) != 0x80 )break;//10ではない
                v |= (c1 & 0x3f) << 12;
                let c2 = u8_ary[ptr8++];
                if( (c2 & 0xc0) != 0x80 )break;//10ではない
                v |= (c2 & 0x3f) << 6;
                let c3 = u8_ary[ptr8++];
                if( (c3 & 0xc0) != 0x80 )break;//10ではない
                v |= (c3 & 0x3f);       
                if( (v & 0x1f0000) == 0 )break;//yyyyyどれか必ず1
                utf32 = v;
                break;
            }
        }while(0);

        // 不正なコード
        if(utf32 <= 0 || utf32 > 0x10ffff)return;

        // キャッシュ書き込み
        if(cptr+2 > str_cache.length){
            str += String.fromCharCode.apply(null,str_cache.slice(0,cptr));
            cptr = 0;
        }

        if(utf32 < 0x10000) {
            str_cache[cptr++] = utf32;
        }else{
            //000u uuuu xxxx xxxx xxxx xxxx
            //1101 10ww wwxx xxxx 1101 11xx xxxx xxxx  ( wwww = uuuuu-1
            let u1 = (utf32 & 0x01f0000) - 0x10000;
            let u2 = (utf32 & 0x000fc00);
            let u3 = (utf32 & 0x00003ff);
            str_cache[cptr++] = 0xd800 + (u1>>10) + (u2>>10);
            str_cache[cptr++] = 0xdc00 + u3;
        }
    }while(ptr8 < len8);

    str += String.fromCharCode.apply(null,str_cache.slice(0,cptr));
    // apply 配列の各要素を引数に分解 スタックに積まれるので数に制限あり

    return str;
}
Rust#[derive(Clone)]
pub enum Token<'a> {
    // 上から解析
    Ret(&'a [char]),    // 改行 '\r\n' 'n' 'r' <br>
    Sp(&'a [char]),     // 空白文字列 ' ' '\t'
    // &???;            // HTMLエスケープ Chに変換
    Id(&'a [char]),     // 識別子 [_a-zA-Z][_a-zA-Z0-9]*
    Num(&'a [char]),    // 数字  [0-9]+
    NotA(&'a [char]),   // アスキー以外の文字列
    Ch(char),           // 1文字
    //SpCh(char),         // 空白1文字
}

impl<'a> Token<'a> {
    pub fn is_skip(&self, skip_ret : bool) -> bool {
        match self {
            Token::Ret(_s) => skip_ret,
            Token::Sp(_s)  => true,
            _ => false,
        }
    }

    pub fn len(&self) -> usize {
        match self {
            Token::Ret(s) 
            | Token::Sp(s)
            | Token::Id(s)
            | Token::Num(s)
            | Token::NotA(s) => s.len(),
            Token::Ch(_c) => 1,       
        }
    }
    pub fn to_string(&self) -> String {
        match self {
            Token::Ret(s) 
            | Token::Sp(s)
            | Token::Id(s)
            | Token::Num(s)
            | Token::NotA(s) => s.into_iter().collect(),
            Token::Ch(c) => c.to_string(),         
        }
    }
    pub fn to_vchar(&self) -> Vec<char> {
        match self {
            Token::Ret(s) 
            | Token::Sp(s)
            | Token::Id(s)
            | Token::Num(s)
            | Token::NotA(s) => Vec::from(*s),
            Token::Ch(c) => vec![*c],
            
        }
    }
    pub fn to_utf16(&self) -> Vec<u16> {
        match self {
            Token::Ret(s) 
            | Token::Sp(s) 
            | Token::Id(s)
            | Token::Num(s)
            | Token::NotA(s) =>  chars_to_utf16(s),
            Token::Ch(c) => char_to_utf16(*c),        
        }       
    }
}

const ESC_AMP : [u16;5] = [38,97,109,112,59];//"&amp;"
const ESC_LT : [u16;4] = [38,108,116,59];//"&lt;"; 
const ESC_GT : [u16;4] = [38,103,116,59];//"&gt;";
fn char_to_utf16(c : char) -> Vec<u16> {
    if c == '&' { return Vec::from(&ESC_AMP[..]);}
    if c == '<' { return Vec::from(&ESC_LT[..]);}
    if c == '>' { return Vec::from(&ESC_GT[..]);}
    let mut b = [0; 2];
    Vec::from(c.encode_utf16(&mut b))
}
fn chars_to_utf16(vc : &[char]) -> Vec<u16> {
    let mut b = [0; 2];
    let mut u = Vec::<u16>::new();
    for c in vc {
        u.extend_from_slice(c.encode_utf16(&mut b));
    }
    u
}

enum Esc{
    Lt,      // &lt; <
    Gt,      // &gt; >
    Amp,     // &amp; & 
    Unkwn,
}

pub struct Context<'ctx>{
    chars : &'ctx [char],
}
impl<'ctx> Context<'ctx> {
    pub fn new(chars : &'ctx [char]) -> Context {
        Context {
            chars : chars,
        }
    }
}

pub struct Reader<'read>{
    ctx : &'read Context<'read>,
    now : char,
    now_pos : usize,   
}

impl<'read> Reader<'read> {
    pub fn new(ctx : &'read Context) -> Reader<'read> {
        Reader{
            ctx : ctx,
            now : ' ',
            now_pos : 0,
        }
    }
    fn is_term(&self) -> bool {
        self.now_pos >= self.ctx.chars.len()
    }
    fn next(&mut self) -> Option<char> {
        if self.is_term() { return None;}
        self.now = self.ctx.chars[self.now_pos];
        self.now_pos += 1;
        return Some(self.now);
    }
    fn getback(&mut self){
        self.now_pos -= 1;
    }
    fn now(&self) -> char {
        self.now
    }

    fn sub_start(&self) -> usize {
        if self.now_pos == 0 { 0 }
        else { self.now_pos - 1 }
    }

    fn sub_to_now(&self, start : usize) -> &'read [char] {
        &self.ctx.chars[start..self.now_pos]
    }

}

pub struct Lexer {
    pub line_no : usize,
    pub html_mode : bool,
}

impl Lexer {
    pub fn new(html_mode : bool) -> Lexer {
        Lexer{
            line_no : 1,
            html_mode : html_mode,
        }
    }

    // コメント処理なし 次のphaseで
    pub fn parse<'a>(&mut self, read : &mut Reader<'a>)
    -> Vec<Token<'a>> {
        let mut tks = Vec::<Token>::new();
        while let Some(tk) = self.lex(read) {
            tks.push(tk);
        }
        return tks;
    }
    
    fn is_space(ch : char) -> bool {
        ch == ' ' || ch == '\t'
    }
    fn is_id_start(ch : char) -> bool {
        ch.is_ascii_alphabetic() || ch =='_'
    }
    fn is_id_ch(ch : char) -> bool {
        ch.is_ascii_alphanumeric() || ch =='_'
    }
    fn lex<'a>(&mut self, read : &mut Reader<'a>) -> Option<Token<'a>> {
        while let Some(ch) = read.next() {
            if ch == '\n' || ch == '\r' {
                return Some(self.crlf(read));
            }else if Self::is_space(ch) {
                return Some(self.space(read));
            }else if self.html_mode && ch == '&' {
                if let Some(tk) = self.escape(read) {
                    return Some(tk);
                }
            }else if self.html_mode && ch == '<' {
                if let Some(tk) = self.tag(read){
                    return Some(tk);
                }
            }else if self.html_mode && ch == '>' {
                //不正タグ 処理終了
                return None;
            }else if Self::is_id_start(ch){
                return self.identifier(read);
            }else if ch.is_ascii_digit() {
                return self.digit(read);    
            }else if !ch.is_ascii() {
                return self.none_ascii(read);
            }else{
                return Some(Token::Ch(ch))
            }
        }
        None
    }

    fn none_ascii<'a>(&mut self, read : &mut Reader<'a>)
    -> Option<Token<'a>> { 
        let start = read.sub_start();
        while let Some(ch) = read.next() {
            if ch.is_ascii() {
                read.getback();
                break;
            }
        }
        Some(Token::NotA(read.sub_to_now(start)))
    }
    fn digit<'a>(&mut self, read : &mut Reader<'a>)
    -> Option<Token<'a>> { 
        let start = read.sub_start();
        
        while let Some(ch) = read.next() {
            if !ch.is_ascii_digit() {
                read.getback();
                break;               
            }
        }
        Some(Token::Num(read.sub_to_now(start)))
    }


    fn identifier<'a>(&mut self, read : &mut Reader<'a>)
    -> Option<Token<'a>> {
        let start = read.sub_start();
        while let Some(ch) = read.next() {
            if !Self::is_id_ch(ch) {
                read.getback();
                break;
            }
        }
        Some(Token::Id(read.sub_to_now(start)))
    }
    fn tag<'a>(&mut self, read : &mut Reader<'a>)
    -> Option<Token<'a>> {
        let start = read.sub_start();
        let mut tn = String::new();
        while let Some(ch) = read.next() {
            if Self::is_space(ch) { read.getback();break; }
            if ch == '>' { break; }
            tn.push(ch);
        }
        if read.now() != '>' {
                while let Some(ch) = read.next() {
                    if ch == '>' { break; }
            }
        }
    
        if tn == "br" || tn == "br/" {
            self.line_no += 1;
            Some(Token::Ret(read.sub_to_now(start)))
        }else{ 
            None// <br>以外は削除
        }
    }

    fn esc_next<'a>(&mut self, read : &mut Reader<'a>) -> Esc {
        let mut r = String::new();
        r.push('&');
        while let Some(ch) = read.next() {
            r.push(ch);
            if ch == ';' {break; }
        }
        let r = r.to_lowercase();
        if r == "&amp;" { Esc::Amp }
        else if r == "&lt;" { Esc::Lt }
        else if r == "&gt;" { Esc::Gt }
        else { Esc::Unkwn }
    }

    fn escape<'a>(&mut self, read : &mut Reader<'a>)
    -> Option<Token<'a>> {
        let esc = self.esc_next(read);
        match esc {
            Esc::Amp => Some(Token::Ch('&')),
            Esc::Lt => Some(Token::Ch('<')),
            Esc::Gt => Some(Token::Ch('>')),
            _ => None,
        }
    }
    
    fn space<'a>(&mut self, read : &mut Reader<'a>) ->  Token<'a> {
        let start = read.sub_start();
        while let Some(ch) = read.next() {
            if !Self::is_space(ch) {
                read.getback();
                break;
            } 
        }

        Token::Sp(read.sub_to_now(start))

    }

    fn crlf<'a>(&mut self, read : &mut Reader<'a>) ->  Token<'a> {
        let start = read.sub_start();
        
        let ch = read.now();
        if ch == '\r' {
            if let Some(ch) = read.next() {
                if ch != '\n' { 
                    read.getback();//CR
                }//else CRLF
            }//else // CR[eof]
        }//else LF
        self.line_no += 1;
        Token::Ret(read.sub_to_now(start))
    }
}
C++#pragma once
#include "btBulletDynamicsCommon.h"
#include "BulletCollision/CollisionDispatch/btGhostObject.h"

namespace Bullet
{

namespace Shared
{
class RefCountBase
{
public:
    RefCountBase() : cntRef(1){}
    virtual ~RefCountBase(){}
    virtual bool Delete() = 0;

    //アトミック操作予定
    void IncRef(){ ++cntRef; }
    int DecRef(){ return --cntRef; }
    void IncConstraintRef(){ ++cntConstrainRef; }
    int DecConstraintRef(){ return --cntConstrainRef; }
    int GetConstraintRef(){ return cntConstrainRef;}
    void SetFlag(int flag){ bitFlag = flag;}
    int GetFlag() const{ return bitFlag;}
    void SetUserPointer(void* p){ pUserPointer = p;}
    void* GetUserPointer()const { return pUserPointer;}
    //アトミック操作予定
private:
    void* pUserPointer = nullptr;
    int cntRef;	//参照カウンタ
    int bitFlag = 0;
    int cntConstrainRef = 0;//拘束カウンタ
};

// デフォルトデリーター
template <class Object_>
class Deleter
{
public:
    void operator()(Object_* p){
        delete p;
    }
};

struct DeleterContrait{};
struct DeleterGhost{};
struct DeleterRigidBody{};

template <class Object_>
struct GetDeleterType
{
    typedef 
    typename std::conditional_t<
        std::is_base_of<btTypedConstraint, Object_>::value,
            DeleterContrait,
    typename std::conditional_t<
        std::is_base_of<btGhostObject, Object_>::value,
            DeleterGhost,
    typename std::conditional_t<
        std::is_base_of<btRigidBody, Object_>::value,
            DeleterRigidBody,
            Object_
    >>> Type;

};


template <class Object_, class Del_ >
class RefCount : public RefCountBase
{
public:
    RefCount(Object_* obj) : pObj(obj)
    {
    }
    virtual ~RefCount(){}
    virtual bool Delete()
    {
        if(DecRef() == 0){
            Del_()(pObj);
            delete this;
            return true;
        }
        return false;
    }
private:
    Object_* pObj;
};



//bulletクラスの直接new delete禁止
//setUserPointer()があるクラスのみ
//new bt???? -> Create(new bt????);
//delete p; -> Relese(p);


// getUserPointer()
// setUserConstraintPtr()
// を使って所有権管理
// テンプレートでどちらもSharedPtrを利用可能に

template<class Object_>
struct UserPointer_getUserPointer
{
    static void* Get(Object_* obj)
    {
        return obj->getUserPointer();
    }
    static void Set(Object_* obj, void* p)
    {
        obj->setUserPointer(p);
    }
};
template<class Object_>
struct UserPointer_getUserConstraintPtr
{
    static void* Get(Object_* obj)
    {
        return obj->getUserConstraintPtr();
    }
    static void Set(Object_* obj, void* p)
    {
        obj->setUserConstraintPtr(p);
    }
};
template <class Object_>
struct SelectUserPointer
{
    typedef
    typename std::conditional_t<
        std::is_base_of<btTypedConstraint, Object_>::value,
        UserPointer_getUserConstraintPtr<Object_>,
        UserPointer_getUserPointer<Object_>
    > Type;
};

template <class Object_>
void* GetUserPointer_(Object_* obj)
{
    if(!obj)return nullptr;
    return SelectUserPointer<Object_>::Type::Get(obj);
}

template <class Object_>
void SetUserPointer_(Object_* obj, void* p){
    if(!obj)return;
    return SelectUserPointer<Object_>::Type::Set(obj,p);
}

template <class Object_>
Object_* Create(Object_* new_Object_)//new Object_();
{
    if(!new_Object_)return nullptr;
    void* uptr = GetUserPointer_(new_Object_);
    if(uptr && uptr != (void*)-1){//(void*)-1 なぜかbtTypedConstraintTypeで-1に初期化
        btAssert(0);//禁止 すでにCreate
        return nullptr;
    }
    RefCountBase* ref = new RefCount<Object_, Deleter<GetDeleterType<Object_>::Type > >(new_Object_);
    SetUserPointer_(new_Object_, ref);
    return new_Object_;
}


template <class Object_>
Object_* AddRef(Object_* obj)
{
    if(!obj)return nullptr;
    RefCountBase* ref = static_cast<RefCountBase*>(GetUserPointer_(obj));
    if(!ref)return nullptr;
    ref->IncRef();
    return obj;
}

template <class Object_>
void Release(Object_* obj)
{
    if(!obj)return;
    RefCountBase* ref = static_cast<RefCountBase*>(GetUserPointer_(obj));
    if(!ref)return;
    if(ref->Delete()){
        return;
    }
}

//getUserPointer代替品
template <class Object_>
void* GetUserPointer(Object_* obj)
{
    if(!obj)return nullptr;
    RefCountBase* ref = static_cast<RefCountBase*>(GetUserPointer_(obj));
    return ref->GetUserPointer();
}
template <class Object_>
void SetUserPointer(Object_* obj, void* ptr)
{
    if(!obj)return;
    RefCountBase* ref = static_cast<RefCountBase*>(GetUserPointer_(obj));
    ref->SetUserPointer(ptr);
}

template <>
class Deleter<DeleterContrait>
{
public:
    void operator()(btTypedConstraint* p){
        if(!p)return;
        btRigidBody* a = &p->getRigidBodyA();
        btRigidBody* b = &p->getRigidBodyB();
        btRigidBody* fix = &p->getFixedBody();
        if(a!=fix)Shared::Release(a);
        if(b!=fix)Shared::Release(b);
        // RigidBodyを1つだけ設定する拘束は内部のstatic変数を参照
        //  ->削除処理しない
        delete p;
    }
};

template <>
class Deleter<DeleterGhost>
{
public:
    void operator()(btGhostObject* p){
        if(!p)return;
        Shared::Release(p->getCollisionShape());
        delete p;
    }
};

template <>
class Deleter<btCompoundShape>
{
public:
    void operator()(btCompoundShape* p){
        if(!p)return;
        int num = p->getNumChildShapes();
        for(int i=0; i<num; ++i){
            Shared::Release(p->getChildShape(i));
        }
        delete p;
    }
};
template <>
class Deleter<DeleterRigidBody>
{
public:
    void operator()(btRigidBody* p){
        if(!p)return;
        delete p->getMotionState();//共有なし 
        Shared::Release(p->getCollisionShape());
        delete p;
    }
};

template <>
class Deleter<btConvexTriangleMeshShape>
{
public:
    void operator()(btConvexTriangleMeshShape* p){
        if(!p)return;
        delete p->getMeshInterface();//共有なし
        delete p;
    }
};

template <>
class Deleter<btBvhTriangleMeshShape>
{
public:
    void operator()(btBvhTriangleMeshShape* p){
        if(!p)return;
        delete p->getTriangleInfoMap();//共有なし
        delete p->getMeshInterface();//共有なし
        delete p;
    }
};


}

template <class Object_>
class SharedPtr
{
public:
    SharedPtr() : pObj(nullptr){}

    template <class Object2_>
    explicit SharedPtr(Object2_* p) : pObj(p)
    {
        Shared::Create<Object2_>(p);
    }
    
    template <class Object2_>
    explicit SharedPtr(Object2_* p, bool refcount) : pObj(p)
    {
        if(refcount)Shared::Create<Object2_>(p);
    }

    SharedPtr(const SharedPtr<Object_>& p)
    {
        pObj = p.AddRef();
    }

    template<class Object2_>
    SharedPtr(const SharedPtr<Object2_>& p)
    {
        pObj = p.AddRef();
    }

    SharedPtr(SharedPtr<Object_>&& p)
    {
        pObj = p.pObj;
        p.pObj = nullptr;
    }
    SharedPtr& operator=(const SharedPtr<Object_>& p)
    {
        Release();
        pObj = p.AddRef();
        return *this;
    }

    SharedPtr& operator=(SharedPtr<Object_>&& p)
    {
        Release();
        pObj = p.pObj;
        p.pObj = nullptr;
        return *this;
    }

    ~SharedPtr()
    {
        Shared::Release(pObj);
    }

    explicit operator bool() const
    {
        return pObj != nullptr;
    }

    Object_ *operator->() const
    {
        return pObj;
    }

    Object_* Get() const
    {
        return pObj;
    }
    typename std::add_lvalue_reference<Object_>::type operator*() const
    {
        *Get();
    }


    void Reset(Object_* p = nullptr)
    {
        Release();
        if(p){
            Shared::Create(p);
        }
        pObj = p;
    }

    template<class Object2_>
    void Reset(Object2_* p = nullptr)
    {
        Release();
        if(p){
            Shared::Create<Object2_>(p);
        }
        pObj = p;
    }

    void Attach(Object_* p, bool addref = false)
    {
        Release();
        if(addref){
            pObj = Shared::AddRef(p);
        }else{
            pObj = p;
        }
    }

    Object_* Detach()
    {
        Object_* ret = pObj;
        pObj = nullptr;
        return ret;
    }


    // ++参照カウンタ bulletクラスへのポインタ設定用
    Object_* AddRef() const
    {
        return Shared::AddRef(pObj);
    }

    void Release()
    {
        Shared::Release(pObj);
        pObj = nullptr;
    }

    void* GetUserPointer()
    {
        return Shared::GetUserPointer(pObj);
    }
    void* SetUserPointer(void* ptr)
    {
        Shared::SetUserPointer(pObj, ptr);
    }
private:
    Object_* pObj;
};

// getUserPointer()付きアクションインターフェイス
class ActionInterface : public btActionInterface{
public:
    ActionInterface(){}

    virtual void preUpdate(btScalar time_frame){}
    virtual void postUpdate(btScalar time_frame){}

    virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep){}
    virtual void debugDraw(btIDebugDraw* debugDrawer){}

    virtual void updateRender(){}
    void* getUserPointer(){ return pUserPointer; }
    void setUserPointer(void* p){ pUserPointer = p; }
private:
    void* pUserPointer = nullptr;
};


}//ns Bullet

バッチコマンド> cargo build --target=wasm32-unknown-unknown --release
> copy target\wasm32-unknown-unknown\release\hello_world.wasm www\
Toml[package]
name = "rshighlite"
version = "0.1.0"
authors = ["zerogram.info"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

[lib]
crate-type = ["cdylib"]

[profile.release]
opt-level = "s"
debug = false
rpath = false
lto = true