diff --git a/.gitignore b/.gitignore deleted file mode 100644 index eb5a316..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target diff --git a/OpenSans-Regular.ttf b/OpenSans-Regular.ttf deleted file mode 100644 index 5263949..0000000 Binary files a/OpenSans-Regular.ttf and /dev/null differ diff --git a/README.md b/README.md deleted file mode 100644 index 949642a..0000000 --- a/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# llama.ttf - -A font containing a large language model. - -## What? - -A font containing a large language model. - -## Why? - -## What? - -## Usage - -Just download [`llama.ttf`](https://github.com/fuglede/llama.ttf/raw/master/llamattf/llama.ttf) and use it like you would any other font. Use it somewhere where [Harfbuzz](https://github.com/harfbuzz/harfbuzz) is used and built with Wasm support. - -## Demo - -Working on it. \ No newline at end of file diff --git a/bin/otfsurgeon b/bin/otfsurgeon deleted file mode 100755 index fa71c44..0000000 --- a/bin/otfsurgeon +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import re -import sys - -from fontTools.misc.testTools import getXML -from fontTools.ttLib import TTFont, newTable - -parser = argparse.ArgumentParser(description="Cut up and rebuild OTF files") -parser.add_argument( - "-i", dest="input", metavar="OTF", help="Input file name", required=True -) - -subparsers = parser.add_subparsers(dest="command", required=True) -parser_strip = subparsers.add_parser("strip", help="Remove one or more tables") -parser_strip.add_argument("-o", dest="output", metavar="OTF", help="Output file name") -parser_strip.add_argument("tables", nargs="*") - -parser_dump = subparsers.add_parser("dump", help="Print table contents to stdout") -parser_dump.add_argument( - "--ttx", dest="ttx", action="store_true", help="Output should be XML, not binary", -) -parser_dump.add_argument("table", action="store") - -parser_add = subparsers.add_parser( - "add", help="Add a binary table to the font from standard input" -) -parser_add.add_argument("-o", dest="output", metavar="OTF", help="Output file name") -parser_add.add_argument("table", action="store") - -parser_steal = subparsers.add_parser( - "steal", help="Copy one or more tables from another font" -) -parser_steal.add_argument("-o", dest="output", metavar="OTF", help="Output file name") -parser_steal.add_argument("fromotf", metavar="FROM_OTF") -parser_steal.add_argument("tables", nargs="*") - -args = parser.parse_args() -font = TTFont(args.input) - - -def strip(): - if not args.output: - args.output = re.sub(r"(\..*?)$", r"-strip\1", args.input) - - for t in args.tables: - if t in font: - del font[t] - else: - print("%s table not found in %s" % (t, args.input), file=sys.stderr) - - print("Writing on %s" % args.output) - font.save(args.output) - - -def dump(): - if args.table not in font: - print("%s table not found in %s" % (args.table, args.input), file=sys.stderr) - sys.exit(1) - if args.ttx: - print("\n".join(getXML(font[args.table].toXML))) - else: - sys.stdout.buffer.write(font[args.table].compile(font)) - - -def steal(): - other = TTFont(args.fromotf) - if not args.output: - args.output = re.sub(r"(\..*?)$", r"-steal\1", args.input) - for t in args.tables: - if t in other: - font[t] = other[t] - else: - print("%s table not found in %s" % (t, args.fromotf), file=sys.stderr) - - print("Writing on %s" % args.output) - try: - font.save(args.output) - except Exception as e: - exception = type(e).__name__ - print("Can't steal those tables: %s: %s" % (exception, e), file=sys.stderr) - if exception == "KeyError": - print("\t(Often this means you don't have the required glyphs in the font)") - sys.exit(1) - - -def add(): - if not args.output: - args.output = re.sub(r"(\..*?)$", r"-add\1", args.input) - data = sys.stdin.buffer.read() - font[args.table] = newTable(args.table) - font[args.table].decompile(data, font) - - print("Writing on %s" % args.output) - try: - font.save(args.output) - except Exception as e: - exception = type(e).__name__ - print("Can't add that table: %s: %s" % (exception, e), file=sys.stderr) - if exception == "KeyError": - print("\t(Often this means you don't have the required glyphs in the font)") - sys.exit(1) - - -globals()[args.command]() # Whoa diff --git a/harfbuzz-wasm/Cargo.toml b/harfbuzz-wasm/Cargo.toml deleted file mode 100644 index fa17de3..0000000 --- a/harfbuzz-wasm/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "harfbuzz-wasm" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -kurbo = { version = "0.9.0", optional = true } diff --git a/harfbuzz-wasm/src/lib.rs b/harfbuzz-wasm/src/lib.rs deleted file mode 100644 index 6921bb0..0000000 --- a/harfbuzz-wasm/src/lib.rs +++ /dev/null @@ -1,468 +0,0 @@ -#![warn(missing_docs)] -#![allow(dead_code)] -//! Interface to Harfbuzz's WASM exports -//! -//! This crate is designed to make it easier to write a -//! WASM shaper for your font using Rust. It binds the -//! functions exported by Harfbuzz into the WASM runtime, -//! and wraps them in an ergonomic interface using Rust -//! structures. For example, here is a basic shaping engine: -//! -//! -//! ```rust -//! #[wasm_bindgen] -//! pub fn shape(font_ref: u32, buf_ref: u32) -> i32 { -//! let font = Font::from_ref(font_ref); -//! let mut buffer = GlyphBuffer::from_ref(buf_ref); -//! for mut item in buffer.glyphs.iter_mut() { -//! // Map character to glyph -//! item.codepoint = font.get_glyph(codepoint, 0); -//! // Set advance width -//! item.h_advance = font.get_glyph_h_advance(item.codepoint); -//! } -//! 1 -//! } -//! ``` -use std::ffi::{c_int, CStr, CString}; - -#[cfg(feature = "kurbo")] -use kurbo::BezPath; - -// We don't use #[wasm_bindgen] here because that makes -// assumptions about Javascript calling conventions. We -// really do just want to import some C symbols and run -// them in unsafe-land! -extern "C" { - fn face_get_upem(face: u32) -> u32; - fn font_get_face(font: u32) -> u32; - fn font_get_glyph(font: u32, unicode: u32, uvs: u32) -> u32; - fn font_get_scale(font: u32, x_scale: *mut i32, y_scale: *mut i32); - fn font_get_glyph_extents(font: u32, glyph: u32, extents: *mut CGlyphExtents) -> bool; - fn font_glyph_to_string(font: u32, glyph: u32, str: *const u8, len: u32); - fn font_get_glyph_h_advance(font: u32, glyph: u32) -> i32; - fn font_get_glyph_v_advance(font: u32, glyph: u32) -> i32; - fn font_copy_glyph_outline(font: u32, glyph: u32, outline: *mut CGlyphOutline) -> bool; - fn face_copy_table(font: u32, tag: u32, blob: *mut Blob) -> bool; - fn buffer_copy_contents(buffer: u32, cbuffer: *mut CBufferContents) -> bool; - fn buffer_set_contents(buffer: u32, cbuffer: &CBufferContents) -> bool; - fn debugprint(s: *const u8); - fn shape_with( - font: u32, - buffer: u32, - features: u32, - num_features: u32, - shaper: *const u8, - ) -> i32; -} - -/// An opaque reference to a font at a given size and -/// variation. It is equivalent to the `hb_font_t` pointer -/// in Harfbuzz. -#[derive(Debug)] -pub struct Font(u32); - -impl Font { - /// Initialize a `Font` struct from the reference provided - /// by Harfbuzz to the `shape` function. - pub fn from_ref(ptr: u32) -> Self { - Self(ptr) - } - /// Call the given Harfbuzz shaper on a buffer reference. - /// - /// For example, `font.shape_with(buffer_ref, "ot")` will - /// run standard OpenType shaping, allowing you to modify - /// the buffer contents after glyph mapping, substitution - /// and positioning has taken place. - pub fn shape_with(&self, buffer_ref: u32, shaper: &str) { - let c_shaper = CString::new(shaper).unwrap(); - unsafe { - shape_with(self.0, buffer_ref, 0, 0, c_shaper.as_ptr() as *const u8); - } - } - - /// Return the font face object that this font belongs to. - pub fn get_face(&self) -> Face { - Face(unsafe { font_get_face(self.0) }) - } - - /// Map a Unicode codepoint to a glyph ID. - /// - /// The `uvs` parameter specifies a Unicode Variation - /// Selector codepoint which is used in conjunction with - /// [format 14 cmap tables](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences) - /// to provide alternate glyph mappings for characters with - /// Unicode Variation Sequences. Generally you will pass - /// `0`. - pub fn get_glyph(&self, unicode: u32, uvs: u32) -> u32 { - unsafe { font_get_glyph(self.0, unicode, uvs) } - } - - /// Get the extents for a given glyph ID, in its design position. - pub fn get_glyph_extents(&self, glyph: u32) -> CGlyphExtents { - let mut extents = CGlyphExtents::default(); - unsafe { - font_get_glyph_extents(self.0, glyph, &mut extents); - } - extents - } - - /// Get the default advance width for a given glyph ID. - pub fn get_glyph_h_advance(&self, glyph: u32) -> i32 { - unsafe { font_get_glyph_h_advance(self.0, glyph) } - } - - /// Get the default vertical advance for a given glyph ID. - fn get_glyph_v_advance(&self, glyph: u32) -> i32 { - unsafe { font_get_glyph_v_advance(self.0, glyph) } - } - - /// Get the name of a glyph. - /// - /// If no names are provided by the font, names of the form - /// `gidXXX` are constructed. - pub fn get_glyph_name(&self, glyph: u32) -> String { - let mut s = [1u8; 32]; - unsafe { - font_glyph_to_string(self.0, glyph, s.as_mut_ptr(), 32); - } - unsafe { CStr::from_ptr(s.as_ptr() as *const _) } - .to_str() - .unwrap() - .to_string() - } - - /// Get the X and Y scale factor applied to this font. - /// - /// This should be divided by the units per em value to - /// provide a scale factor mapping from design units to - /// user units. (See [`Face::get_upem`].) - pub fn get_scale(&self) -> (i32, i32) { - let mut x_scale: i32 = 0; - let mut y_scale: i32 = 0; - unsafe { - font_get_scale( - self.0, - &mut x_scale as *mut c_int, - &mut y_scale as *mut c_int, - ) - }; - (x_scale, y_scale) - } - - #[cfg(feature = "kurbo")] - /// Get the outline of a glyph as a vector of bezier paths - pub fn get_outline(&self, glyph: u32) -> Vec { - let mut outline = CGlyphOutline { - n_points: 0, - points: std::ptr::null_mut(), - n_contours: 0, - contours: std::ptr::null_mut(), - }; - let end_pts_of_contours: &[usize] = unsafe { - font_copy_glyph_outline(self.0, glyph, &mut outline); - std::slice::from_raw_parts(outline.contours, outline.n_contours as usize) - }; - let points: &[CGlyphOutlinePoint] = - unsafe { std::slice::from_raw_parts(outline.points, outline.n_points as usize) }; - let mut results: Vec = vec![]; - let mut start_pt: usize = 0; - for end_pt in end_pts_of_contours { - let this_contour = &points[start_pt..*end_pt]; - start_pt = *end_pt; - let mut path = BezPath::new(); - let mut ix = 0; - while ix < this_contour.len() { - let point = &this_contour[ix]; - match point.pointtype { - PointType::MoveTo => path.move_to((point.x as f64, point.y as f64)), - PointType::LineTo => path.line_to((point.x as f64, point.y as f64)), - PointType::QuadraticTo => { - ix += 1; - let end_pt = &this_contour[ix]; - path.quad_to( - (point.x as f64, point.y as f64), - (end_pt.x as f64, end_pt.y as f64), - ); - } - PointType::CubicTo => { - ix += 1; - let mid_pt = &this_contour[ix]; - ix += 1; - let end_pt = &this_contour[ix]; - path.curve_to( - (point.x as f64, point.y as f64), - (mid_pt.x as f64, mid_pt.y as f64), - (end_pt.x as f64, end_pt.y as f64), - ); - } - } - ix += 1; - } - path.close_path(); - results.push(path); - } - results - } -} - -/// An opaque reference to a font face, equivalent to the `hb_face_t` pointer -/// in Harfbuzz. -/// -/// This is generally returned from [`Font::get_face`]. -#[derive(Debug)] -pub struct Face(u32); - -impl Face { - /// Get a blob containing the contents of the given binary font table. - pub fn reference_table(&self, tag: &str) -> Blob { - let mut tag_u: u32 = 0; - let mut chars = tag.chars(); - tag_u |= (chars.next().unwrap() as u32) << 24; - tag_u |= (chars.next().unwrap() as u32) << 16; - tag_u |= (chars.next().unwrap() as u32) << 8; - tag_u |= chars.next().unwrap() as u32; - let mut blob = Blob { - data: std::ptr::null_mut(), - length: 0, - }; - unsafe { - face_copy_table(self.0, tag_u, &mut blob); - } - blob - } - - /// Get the face's design units per em. - pub fn get_upem(&self) -> u32 { - unsafe { face_get_upem(self.0) } - } -} - -/// Trait implemented by custom structs representing buffer items -pub trait BufferItem { - /// Construct an item in your preferred representation out of the info and position data provided by Harfbuzz. - fn from_c(info: CGlyphInfo, position: CGlyphPosition) -> Self; - /// Return info and position data to Harfbuzz. - fn to_c(self) -> (CGlyphInfo, CGlyphPosition); -} - -/// Generic representation of a Harfbuzz buffer item. -/// -/// By making this generic, we allow you to implement your own -/// representations of buffer items; for example, in your shaper, -/// you may want certain fields to keep track of the glyph's name, -/// extents, or shape, so you would want a custom struct to represent -/// buffer items. If you don't care about any of them, use the -/// supplied `GlyphBuffer` struct. -#[derive(Debug)] -pub struct Buffer { - _ptr: u32, - /// Glyphs in the buffer - pub glyphs: Vec, -} - -impl Buffer { - /// Construct a buffer from the pointer Harfbuzz provides to the WASM. - /// - /// The `Buffer` struct implements Drop, meaning that when the shaping - /// function is finished, the buffer contents are sent back to Harfbuzz. - pub fn from_ref(ptr: u32) -> Self { - let mut c_contents = CBufferContents { - info: std::ptr::null_mut(), - position: std::ptr::null_mut(), - length: 0, - }; - - unsafe { - if !buffer_copy_contents(ptr, &mut c_contents) { - panic!("Couldn't copy buffer contents") - } - }; - let positions: Vec = unsafe { - std::slice::from_raw_parts(c_contents.position, c_contents.length as usize).to_vec() - }; - let infos: Vec = unsafe { - std::slice::from_raw_parts(c_contents.info, c_contents.length as usize).to_vec() - }; - Buffer { - glyphs: infos - .into_iter() - .zip(positions.into_iter()) - .map(|(i, p)| T::from_c(i, p)) - .collect(), - _ptr: ptr, - } - } -} - -impl Drop for Buffer { - fn drop(&mut self) { - let mut positions: Vec; - let mut infos: Vec; - let glyphs = std::mem::take(&mut self.glyphs); - (infos, positions) = glyphs.into_iter().map(|g| g.to_c()).unzip(); - let c_contents = CBufferContents { - length: positions.len() as u32, - info: infos[..].as_mut_ptr(), - position: positions[..].as_mut_ptr(), - }; - unsafe { - if !buffer_set_contents(self._ptr, &c_contents) { - panic!("Couldn't set buffer contents"); - } - } - } -} - -/// Some data provided by Harfbuzz. -#[derive(Debug)] -#[repr(C)] -pub struct Blob { - /// Length of the blob in bytes - pub length: u32, - /// A raw pointer to the contents - pub data: *mut u8, -} - -#[allow(missing_docs)] -/// Glyph information in a buffer item provided by Harfbuzz -/// -/// You'll only need to interact with this if you're writing -/// your own buffer item structure. -#[repr(C)] -#[derive(Debug, Clone)] -pub struct CGlyphInfo { - pub codepoint: u32, - pub mask: u32, - pub cluster: u32, - pub var1: u32, - pub var2: u32, -} - -#[allow(missing_docs)] -/// Glyph positioning information in a buffer item provided by Harfbuzz -/// -/// You'll only need to interact with this if you're writing -/// your own buffer item structure. -#[derive(Debug, Clone)] -#[repr(C)] -pub struct CGlyphPosition { - pub x_advance: i32, - pub y_advance: i32, - pub x_offset: i32, - pub y_offset: i32, - pub var: u32, -} - -/// Glyph extents -#[derive(Debug, Clone, Default)] -#[repr(C)] -pub struct CGlyphExtents { - /// The scaled left side bearing of the glyph - pub x_bearing: i32, - /// The scaled coordinate of the top of the glyph - pub y_bearing: i32, - /// The width of the glyph - pub width: i32, - /// The height of the glyph - pub height: i32, -} - -#[derive(Debug)] -#[repr(C)] -struct CBufferContents { - length: u32, - info: *mut CGlyphInfo, - position: *mut CGlyphPosition, -} - -/// Ergonomic representation of a Harfbuzz buffer item -/// -/// Harfbuzz buffers are normally split into two arrays, -/// one representing glyph information and the other -/// representing glyph positioning. In Rust, this would -/// require lots of zipping and unzipping, so we zip them -/// together into a single structure for you. -#[derive(Debug, Clone, Copy)] -pub struct Glyph { - /// The Unicode codepoint or glyph ID of the item - pub codepoint: u32, - /// The index of the cluster in the input text where this came from - pub cluster: u32, - /// The horizontal advance of the glyph - pub x_advance: i32, - /// The vertical advance of the glyph - pub y_advance: i32, - /// The horizontal offset of the glyph - pub x_offset: i32, - /// The vertical offset of the glyph - pub y_offset: i32, - /// You can use this for whatever you like - pub flags: u32, -} -impl BufferItem for Glyph { - fn from_c(info: CGlyphInfo, pos: CGlyphPosition) -> Self { - Self { - codepoint: info.codepoint, - cluster: info.cluster, - x_advance: pos.x_advance, - y_advance: pos.y_advance, - x_offset: pos.x_offset, - y_offset: pos.y_offset, - flags: 0, - } - } - fn to_c(self) -> (CGlyphInfo, CGlyphPosition) { - let info = CGlyphInfo { - codepoint: self.codepoint, - cluster: self.cluster, - mask: 0, - var1: 0, - var2: 0, - }; - let pos = CGlyphPosition { - x_advance: self.x_advance, - y_advance: self.y_advance, - x_offset: self.x_offset, - y_offset: self.y_offset, - var: 0, - }; - (info, pos) - } -} - -#[repr(C)] -#[allow(clippy::enum_variant_names)] -#[derive(Clone, Debug)] -enum PointType { - MoveTo, - LineTo, - QuadraticTo, - CubicTo, -} - -#[repr(C)] -#[derive(Clone, Debug)] -struct CGlyphOutlinePoint { - x: f32, - y: f32, - pointtype: PointType, -} - -#[repr(C)] -struct CGlyphOutline { - n_points: usize, - points: *mut CGlyphOutlinePoint, - n_contours: usize, - contours: *mut usize, -} - -/// Our default buffer item struct. See also [`Glyph`]. -pub type GlyphBuffer = Buffer; - -/// Write a string to the Harfbuzz debug log. -pub fn debug(s: &str) { - let c_s = CString::new(s).unwrap(); - unsafe { - debugprint(c_s.as_ptr() as *const u8); - }; -} diff --git a/index.html b/index.html new file mode 100644 index 0000000..548fc65 --- /dev/null +++ b/index.html @@ -0,0 +1,91 @@ + + + + + llama.ttf + + + + +
+

llama.ttf

+

llama.ttf is a font file which is also a large language model and an inference engine for that model.

+

Ehm, what?

+

llama.ttf is a font file which is also a large language model and an inference engine for that model.

+

Why?

+

How?

+

+ The font shaping engine Harfbuzz, used in applications such as Firefox and Chrome, + comes with a Wasm shaper allowing arbitrary code to be + used to "shape" text. + +

+ In particular, this "arbitrary" code could in principle be an entire LLM inference engine (Llama in our case, + hence the name). +

+ It could also in principle be an entire LLM inference engine but not just in principle, relying on treating text containing magic + symbols for fake "ligatures" to initialize the LLM and use it to generate text. +

+

Okay show me

+

Yeah, sorry, that was brief. Here's an attempt to make it make more sense:

+ +

Skip to 6:09 if you just want to see the font in action.

+

Usage

+

Just download llama.ttf and use it like you would any other font. Use it somewhere where Harfbuzz is used and built with Wasm support.

+
+ + + diff --git a/llamattf/Cargo.lock b/llamattf/Cargo.lock deleted file mode 100644 index cb5671a..0000000 --- a/llamattf/Cargo.lock +++ /dev/null @@ -1,158 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "harfbuzz-wasm" -version = "0.1.0" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "libc" -version = "0.2.155" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" - -[[package]] -name = "llama2ttf" -version = "0.1.0" -dependencies = [ - "harfbuzz-wasm", - "num_cpus", - "true", - "wasm-bindgen", -] - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "true" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "548ec159e98411c04bee9b458df4ccd2da2a0a5e50c08cbd7e90b00b154c5a9a" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" diff --git a/llamattf/Cargo.toml b/llamattf/Cargo.toml deleted file mode 100644 index 01e5b72..0000000 --- a/llamattf/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "llama2ttf" -version = "0.1.0" -edition = "2021" - - -[dependencies] -num_cpus = { version = "1.16.0", optional = true } -true = { version = "0.1.0", optional = true } -harfbuzz-wasm = { path = "../harfbuzz-wasm"} -wasm-bindgen = "0.2.87" - -[lib] -crate-type = ["cdylib"] - - -[profile.release] -panic = "abort" - diff --git a/llamattf/Makefile b/llamattf/Makefile deleted file mode 100644 index fb69b5f..0000000 --- a/llamattf/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -BASE_FONT=../OpenSans-Regular.ttf -TARGET_FONT=llama.ttf -WASM=llama2ttf_bg.wasm - -$(TARGET_FONT): pkg/$(WASM) $(BASE_FONT) - ../bin/otfsurgeon -i $(BASE_FONT) add -o $(TARGET_FONT) Wasm < pkg/$(WASM) - -pkg/$(WASM): src/lib.rs - wasm-pack build --target web diff --git a/llamattf/llama.ttf b/llamattf/llama.ttf deleted file mode 100644 index 6d4cdad..0000000 Binary files a/llamattf/llama.ttf and /dev/null differ diff --git a/llamattf/src/lib.rs b/llamattf/src/lib.rs deleted file mode 100644 index 0dd6ec0..0000000 --- a/llamattf/src/lib.rs +++ /dev/null @@ -1,87 +0,0 @@ -use harfbuzz_wasm::{debug, Font, Glyph, GlyphBuffer}; -use wasm_bindgen::prelude::*; - -pub mod llama2; - -fn next_n_words(s: &str, seq_len: usize) -> String { - let config = llama2::Config::from_file(); - let mut state = llama2::ExecutionState::>::init(&config); - let vocab = llama2::Vocab::from_file(config.vocab_size); - let mut weights = llama2::LlamaWeights::load_weights(&config); - // Token 1 is the starting token. TODO: Use the user's input as starting tokens - // instead. This is totally possible using e.g. the sentencepiece crate to - // encode tokens, but it does unfortunately seem to be hard to use with Wasm, - // and a custom encoder owuld be better. - let mut tokens = vec![1]; - let mut pos = 1; - let mut token = 1; - for token_us in tokens.as_slice() { - token = *token_us; - llama2::LamaExecuter::step(&mut weights, token, pos, &config, &mut state); - pos += 1; - } - let initial_tokens = tokens.len(); - tokens = vec![]; - while pos < seq_len + initial_tokens { - let next = state - .logits - .iter() - .enumerate() - .max_by(|(_, a), (_, b)| a.total_cmp(b)) - .map(|(index, _)| index) - .unwrap(); - tokens.push(next); - token = next; - llama2::LamaExecuter::step(&mut weights, token, pos, &config, &mut state); - pos += 1; - } - - let result = tokens.into_iter().map(|x| vocab.get_token(x).to_string()).collect::>().join(""); - result.replace("\n", " ") -} - -#[wasm_bindgen] -pub fn shape( - _shape_plan: u32, - font_ref: u32, - buf_ref: u32, - _features: u32, - _num_features: u32, -) -> i32 { - let font = Font::from_ref(font_ref); - let mut buffer = GlyphBuffer::from_ref(buf_ref); - // Get buffer as string - let buf_u8: Vec = buffer.glyphs.iter().map(|g| g.codepoint as u8).collect(); - let str_buf = String::from_utf8_lossy(&buf_u8); - let res_str = if str_buf.starts_with("Once upon a time!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") { - let count = str_buf.chars().filter(|c| *c == '!').count() as usize; - let s = format!("{}", next_n_words(&str_buf, count + 5 - 70)); - debug(&s); - s - } else if str_buf.starts_with("Abracadabra") || str_buf.starts_with("Once upon") { - format!("{}", str_buf).replace("ö", "ø") - } else { - format!("{}", str_buf).replace("Open", "LLaMa").replace("ö", "ø").replace("o", "ø") - }; - buffer.glyphs = res_str.chars() - .enumerate() - .map(|(ix, x)| Glyph { - codepoint: x as u32, - flags: 0, - x_advance: 0, - y_advance: 0, - cluster: ix as u32, - x_offset: 0, - y_offset: 0, - }) - .collect(); - - for mut item in buffer.glyphs.iter_mut() { - // Map character to glyph - item.codepoint = font.get_glyph(item.codepoint, 0); - // Set advance width - item.x_advance = font.get_glyph_h_advance(item.codepoint); - } - // Buffer is written back to HB on drop - 1 -} \ No newline at end of file diff --git a/llamattf/src/llama2.rs b/llamattf/src/llama2.rs deleted file mode 100644 index 90a3438..0000000 --- a/llamattf/src/llama2.rs +++ /dev/null @@ -1,596 +0,0 @@ -// Adapted from https://github.com/gaxler/llama2.rs/ -// -// MIT License -// -// Copyright (c) 2023 Andrej -// -// 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. - -use std::mem; - - -static STORIES: &[u8; 60816028] = include_bytes!("stories15M.bin"); // Yup ... -static TOKENIZER: &[u8; 304713] = include_bytes!("tokenizer.bin"); - - -const CONF_VALS: usize = 7; -const CONF_SIZE: usize = std::mem::size_of::<[i32; CONF_VALS]>(); - -#[derive(Debug, Clone, Copy)] -pub struct Config { - dim: usize, - hidden_dim: usize, - n_layers: usize, - n_heads: usize, - n_kv_heads: usize, - pub vocab_size: usize, - seq_len: usize, - shared_weights: bool, -} - -/// Exexute LLama step -pub trait LamaExecuter { - fn step(&mut self, token: usize, pos: usize, cfg: &Config, state: &mut ExecutionState); -} - -/// Executte Llama layer -pub trait LLamaLayer { - /// RMS norm residual stream and get Q,K,V matrices - fn rms_and_qkv(&self, cfg: &Config, state: &mut ExecutionState); - /// Rotate q and k heads according to position in seq (RoPE) - fn rope( - &self, - pos: usize, - cfg: &Config, - state: &mut ExecutionState, - rope_imag: &Buffer, - rope_real: &Buffer, - ); - /// Cache sequence of Q, K (to be used for attention computation) - fn cache_kv(&mut self, pos: usize, cfg: &Config, state: &ExecutionState); - /// (per head) Calculate Attention weights, accumulate value according to weights - fn attention(&self, pos: usize, cfg: &Config, state: &ExecutionState); - /// Merge all heads and add result to residula stream - fn merge_heads_to_resid_stream(&self, state: &mut ExecutionState); - /// RMS norm residual stream, - /// apply FeedForward to normalized - /// add to residual stream - fn ffn(&self, state: &mut ExecutionState); -} - -pub trait LinearWeight { - fn mat_vec(&self, vec: &T, dst: &mut T); -} -pub trait RMSNormWeight { - fn rms_norm(&self, vec: &T, out: &mut T); - fn inplace_rms_norm(&self, vec: &mut T); -} - -pub trait EmbeddingTable: LinearWeight { - fn token_to_resid_stream(&self, token: usize, dst: &mut Buf, cfg: &Config); -} - -pub struct LlamaWeights { - /// (vocab_size, dim) - pub embeddings: Emb, - pub layers: Vec, - /// (dim,) - pub rms_final: Rms, - /// (seq_len, head_size/2) - pub rope_real: Buf, - /// (seq_len, head_size/2) - pub rope_imag: Buf, - pub wcls: Option, -} - -pub struct LayerWeights { - rms_attn: Rms, - rms_ffn: Rms, - wq: Lin, - wk: Lin, - wv: Lin, - wo: Lin, - w1: Lin, - w2: Lin, - w3: Lin, - /// (seq_len, dim) - k_cache: Buf, - /// (seq_len, dim) - v_cache: Buf, -} - -type Ty = f32; -type CPULayerFloat = LayerWeights, Vec, Vec>; -type Llama2CPUFloat = LlamaWeights, Vec, Vec>; - -pub struct ExecutionState { - /// Shape:(dim,) - x: Buffer, - /// Shape:(dim,) - xb: Buffer, - /// Shape:(dim,) - xb2: Buffer, - /// Shape:(hidden_dim,) - h1: Buffer, - /// Shape:(hidden_dim,) - h2: Buffer, - /// (dim,): Q, buffers - q: Buffer, - /// (dim,): K buffer - k: Buffer, - /// (dim,): V buffer - v: Buffer, - /// (n_heads, seq_len): Attention Weight Buffer - att: Buffer, - /// Logits: (vocab_size, ) - pub logits: Buffer, -} - -// f32 CPU implementation of Llama2 -impl LamaExecuter> for LlamaWeights> -where - L: LLamaLayer>, - Rms: RMSNormWeight>, - Emb: EmbeddingTable>, -{ - fn step( - &mut self, - token: usize, - pos: usize, - cfg: &Config, - state: &mut ExecutionState>, - ) { - // copy token embedding to residual stream - self.embeddings - .token_to_resid_stream(token, &mut state.x, cfg); - - for ld in self.layers.iter_mut() { - ld.rms_and_qkv(cfg, state); - ld.rope(pos, cfg, state, &self.rope_imag, &self.rope_real); - ld.cache_kv(pos, cfg, state); - ld.attention(pos, cfg, state); - ld.merge_heads_to_resid_stream(state); - ld.ffn(state); - } - - self.rms_final.inplace_rms_norm(&mut state.x); - - if self.wcls.is_none() { - self.embeddings.mat_vec(&state.x, &mut state.logits); - } else { - let w = self.wcls.as_ref().unwrap(); - w.mat_vec(&state.x, &mut state.logits); - } - } -} - -// f32 Implementation of Llama2 layer -impl LLamaLayer> for LayerWeights> -where - Lin: LinearWeight>, - Rms: RMSNormWeight>, -{ - fn rms_and_qkv(&self, cfg: &Config, state: &mut ExecutionState>) { - self.rms_attn.rms_norm(&state.x, &mut state.xb); - self.wq.mat_vec(&state.xb, &mut state.q); - self.wk.mat_vec(&state.xb, &mut state.k); - self.wv.mat_vec(&state.xb, &mut state.v); - } - fn rope( - &self, - pos: usize, - cfg: &Config, - state: &mut ExecutionState>, - rope_imag: &Vec, - rope_real: &Vec, - ) { - let head_size = cfg.dim / cfg.n_heads; - - let q_heads = state.q.chunks_exact_mut(head_size); - let k_heads = state.k.chunks_exact_mut(head_size); - - for (q, k) in q_heads.zip(k_heads) { - let mut re = rope_real[pos * head_size / 2..].iter().take(head_size / 2); - - let mut im = rope_imag[pos * head_size / 2..].iter().take(head_size / 2); - - for (qq, kk) in q.chunks_exact_mut(2).zip(k.chunks_exact_mut(2)) { - let (q0, q1) = (qq[0], qq[1]); - let (k0, k1) = (kk[0], kk[1]); - let fcr = re.next().unwrap(); - let fci = im.next().unwrap(); - qq[0] = q0 * fcr - q1 * fci; - qq[1] = q0 * fci + q1 * fcr; - kk[0] = k0 * fcr - k1 * fci; - kk[1] = k0 * fci + k1 * fcr; - } - } - } - fn cache_kv(&mut self, pos: usize, cfg: &Config, state: &ExecutionState>) { - let dst_k = &mut self.k_cache[pos * cfg.dim..(pos + 1) * cfg.dim]; - let dst_v = &mut self.v_cache[pos * cfg.dim..(pos + 1) * cfg.dim]; - dst_k.copy_from_slice(&state.k); - dst_v.copy_from_slice(&state.v); - } - - fn attention(&self, pos: usize, cfg: &Config, state: &ExecutionState>) { - // State is a shared reference becasue we will pass that to multiple threads. - // However we are going to take an unsafe mutable references inside the threads - // We can do that because each thread handles a single head and head data is disjoint - let head_size = cfg.dim / cfg.n_heads; - let k_cache = self.k_cache.as_slice(); - let v_cache = self.v_cache.as_slice(); - - let attn_lambda = |h: usize| { - let q = unsafe { _uncheked_slice(&state.q, h * head_size, head_size) }; - // head attention weights of len (seq_len, ) - let att_weights = - unsafe { _uncheked_mut_slice(&state.att, h * cfg.seq_len, cfg.seq_len) }; - let xb = unsafe { _uncheked_mut_slice(&state.xb, h * head_size, head_size) }; - // head K cache of (seq_len,head_size) - let mut head_k_cache = k_cache.chunks_exact(head_size).skip(h).step_by(cfg.n_heads); - - // do for head - for t in 0..=pos { - let k = head_k_cache.next().unwrap(); // head_size - let score = k - .iter() - .zip(q.iter()) - .fold(0 as Ty, |acc, (_k, _q)| acc + _k * _q); - let score = score / (head_size as Ty).sqrt(); - unsafe { - *att_weights.get_unchecked_mut(t) = score; - } - } - - // head V cache of (seq_len, head_size) - let head_v_cache = v_cache.chunks_exact(head_size).skip(h).step_by(cfg.n_heads); - inplace_softmax(&mut att_weights[..=pos]); - // reset buffer head out buffer - xb.iter_mut().for_each(|v| *v = 0 as Ty); - // accumulate cached values to current buffer - // according to attention prob. (normalized weights) - for (vals, p_attn) in head_v_cache.zip(att_weights.iter()).take(pos + 1) { - vals.iter() - .zip(xb.iter_mut()) - .for_each(|(v, dst)| *dst += v * p_attn) - } - }; - - #[cfg(feature = "parallel")] - (0..cfg.n_heads).into_par_iter().for_each(attn_lambda); - - #[cfg(not(feature = "parallel"))] - (0..cfg.n_heads).for_each(attn_lambda); - } - - fn merge_heads_to_resid_stream(&self, state: &mut ExecutionState>) { - // merge heads - // at this point result of all heads in in x[1], - // Linearly merge all heads into a new buffer x[2] - self.wo.mat_vec(&state.xb, &mut state.xb2); - - // add attention result to residual stream - state - .x - .iter_mut() - .zip(state.xb2.iter()) - .for_each(|(x, xb)| *x += *xb); - } - - fn ffn(&self, state: &mut ExecutionState>) { - // normalize residual stream before FFN - self.rms_ffn.rms_norm(&state.x, &mut state.xb); - - // FFN: - // z = SiLU(W1 \dot x) * (W3 \dot x) - // out = (W2 \dot z) - self.w1.mat_vec(&state.xb, &mut state.h1); - self.w3.mat_vec(&state.xb, &mut state.h2); - - // silu hidden - for h1 in state.h1.iter_mut() { - // 1 / 1 + exp(-hv) - let _scaler = (1 as Ty) / ((1 as Ty) + (-*h1).exp()); - *h1 = *h1 * _scaler; - } - - // combine hidden state with multiplication - for (h1, &h2) in state.h1.iter_mut().zip(state.h2.iter()) { - *h1 *= h2; - } - self.w2.mat_vec(&state.h1, &mut state.xb); - - // add FFN result to residual stream - state - .x - .iter_mut() - .zip(state.xb.iter()) - .for_each(|(x, z)| *x += *z); - } -} - -/// Helper to simplifiy buffer init -pub trait DefualtBuffer { - fn zeros(size: usize) -> Self; -} - -impl DefualtBuffer for Vec { - fn zeros(size: usize) -> Self { - vec![0 as Ty; size] - } -} - -impl ExecutionState { - pub fn init(cfg: &Config) -> Self { - Self { - x: T::zeros(cfg.dim), - xb: T::zeros(cfg.dim), - xb2: T::zeros(cfg.dim), - h1: T::zeros(cfg.hidden_dim), - h2: T::zeros(cfg.hidden_dim), - q: T::zeros(cfg.dim), - k: T::zeros(cfg.dim), - v: T::zeros(cfg.dim), - att: T::zeros(cfg.n_heads * cfg.seq_len), - logits: T::zeros(cfg.vocab_size), - } - } -} - -impl EmbeddingTable> for Vec { - fn token_to_resid_stream(&self, pos: usize, dst: &mut Vec, cfg: &Config) { - let dim = dst.len(); - self.chunks_exact(dim) - .skip(pos) - .take(1) - .for_each(|src| dst.as_mut_slice().copy_from_slice(src)); - } -} - -impl LinearWeight> for Vec { - fn mat_vec(&self, vec: &Vec, dst: &mut Vec) { - matmul(dst, vec, &self); // in_dim is infered form x. need to remove from function sig - } -} - -#[inline] -fn _norm_const(vec: &[Ty]) -> Ty { - let dim = vec.len() as Ty; - let ssq = vec.iter().fold(0f32, |init, &v| init + v * v) / dim; - (1 as Ty) / (ssq + 1e-5).sqrt() -} - -impl RMSNormWeight> for Vec { - fn rms_norm(&self, vec: &Vec, out: &mut Vec) { - let inv_denom = _norm_const(vec); - - let w_it = self.iter(); - let normed = vec.iter().zip(w_it).map(|(xx, ww)| xx * ww * inv_denom); - out.iter_mut().zip(normed).for_each(|(dst, src)| *dst = src); - } - - fn inplace_rms_norm(&self, vec: &mut Vec) { - let inv_denom = _norm_const(vec); - - let w_it = self.iter(); - vec.iter_mut() - .zip(w_it) - .for_each(|(dst, w)| (*dst) *= inv_denom * w); - } -} - -fn _alloc_and_read(offset: usize, size: usize) -> Vec { - unsafe { - let float_ptr = STORIES.as_ptr() as *const Ty; - let data = std::slice::from_raw_parts(float_ptr.add(offset), size); - data.to_vec() - } -} - -/// Load raw weights from Karphaty's models -fn load_raw_karphaty(cfg: &Config) -> (Vec>, Option>) { - let f = |offset: usize, s: usize| _alloc_and_read(offset, s); - let head_size = cfg.dim / cfg.n_heads; - - let sizes = vec![ - cfg.vocab_size * cfg.dim, - cfg.n_layers * cfg.dim, - cfg.n_layers * cfg.dim * cfg.dim, - cfg.n_layers * cfg.dim * cfg.dim, - cfg.n_layers * cfg.dim * cfg.dim, - cfg.n_layers * cfg.dim * cfg.dim, - cfg.n_layers * cfg.dim, - cfg.n_layers * cfg.dim * cfg.hidden_dim, - cfg.n_layers * cfg.dim * cfg.hidden_dim, - cfg.n_layers * cfg.dim * cfg.hidden_dim, - cfg.dim, - cfg.seq_len * (head_size / 2), - cfg.seq_len * (head_size / 2), - ]; - let mut offset = 7; - let mut parameters: Vec> = vec![]; - for size in sizes { - parameters.push(f(offset, size)); - offset += size; - } - - ( - parameters, - cfg.shared_weights.then(|| f(offset, cfg.vocab_size * cfg.dim)), - ) - - -} - - -impl Llama2CPUFloat { - - pub fn load_weights(cfg: &Config) -> Self { - let (weights, wcls) = load_raw_karphaty(cfg); - let embeddings = weights[0].clone(); - - // Go over all layered weights, and make layer chunk out of them - let mut w_layer_iters = weights[1..10] - .iter() - .map(|v| { - let csize = v.len() / cfg.n_layers; - v.chunks(csize).map(|l| l.to_vec()) - }) - .collect::>(); - - let layers = (0..cfg.n_layers) - .map(|l| LayerWeights::, Vec, Vec> { - rms_attn: w_layer_iters[0].next().unwrap(), - wq: w_layer_iters[1].next().unwrap(), - wk: w_layer_iters[2].next().unwrap(), - wv: w_layer_iters[3].next().unwrap(), - wo: w_layer_iters[4].next().unwrap(), - rms_ffn: w_layer_iters[5].next().unwrap(), - w1: w_layer_iters[6].next().unwrap(), - w2: w_layer_iters[7].next().unwrap(), - w3: w_layer_iters[8].next().unwrap(), - k_cache: vec![0 as Ty; cfg.seq_len * cfg.dim], - v_cache: vec![0 as Ty; cfg.seq_len * cfg.dim], - }) - .collect(); - - let rms_final = weights[10].clone(); - let rope_real = weights[11].clone(); - let rope_imag = weights[12].clone(); - - Self { - embeddings, - layers, - rms_final, - rope_real, - rope_imag, - wcls, - } - } -} - - - -impl Config { - /// Read raw bytes and force those to be our config type (which conforms to C mem layout) - pub fn from_file() -> Self { - let mut buffer = [0; CONF_SIZE]; - for j in 0..28 { - buffer[j] = STORIES[j]; - } - //model_bin.read_exact(&mut buffer).unwrap(); - let raw_conf = unsafe { mem::transmute::<[u8; CONF_SIZE], [i32; CONF_VALS]>(buffer) }; - let (vocab_size, shared_weights) = if raw_conf[5] < 0 { - (-raw_conf[5] as usize, true) - } else { - (raw_conf[5] as usize, false) - }; - - Self { - dim: raw_conf[0] as usize, - hidden_dim: raw_conf[1] as usize, - n_layers: raw_conf[2] as usize, - n_heads: raw_conf[3] as usize, - n_kv_heads: raw_conf[4] as usize, - vocab_size, - seq_len: raw_conf[6] as usize, - shared_weights, - } - } -} - -pub struct Vocab { - bytes: Vec, - offsets: Vec, -} - -impl Vocab { - pub fn from_file(vocab_size: usize) -> Self { - let mut bytes = Vec::::new(); - let mut offsets = vec![0usize; 1]; - let vocab_bytes = TOKENIZER; - let mut pointer = 0; - let mut len = [0; 4]; - let mut val = [0; 1]; - for vs in 0..vocab_size { - for j in 0..4 { - len[j] = vocab_bytes[pointer+j]; - } - pointer += 4; - //vocab_bin.read_exact(&mut len).unwrap(); - let l = unsafe { mem::transmute::<[u8; 4], i32>(len) }; - offsets.push(offsets.last().unwrap() + l as usize); - (0..l).for_each(|_| { - val[0] = vocab_bytes[pointer]; - pointer += 1; - // vocab_bin.read_exact(&mut val).unwrap(); - bytes.extend(val); - }); - } - - assert_eq!(offsets.len(), vocab_size + 1); - - Self { bytes, offsets } - } - - pub fn get_token(&self, idx: usize) -> &str { - let (st, en) = (self.offsets[idx], self.offsets[idx + 1]); - let b = &self.bytes[st..en]; - std::str::from_utf8(b).unwrap() - } -} - - -#[cfg(not(feature = "parallel"))] -fn matmul(out: &mut [Ty], x: &[Ty], w: &[Ty]) { - let stride = x.len(); - for (row, out_elem) in w.chunks_exact(stride).zip(out.iter_mut()) { - let val = row - .iter() - .zip(x.iter()) - .fold(0 as Ty, |acc, (&_w, &_x)| acc + _w * _x); - *out_elem = val; - } -} - -/// We can safely borrow disjoint parts of slices, but its really hard for the borrow checker to know that this is safe -unsafe fn _uncheked_mut_slice(s: &[Ty], offset: usize, size: usize) -> &mut [Ty] { - let ptr: *mut f32 = s.as_ptr() as *mut Ty; - let st = ptr.add(offset); - std::slice::from_raw_parts_mut(st, size) -} - -/// We can safely borrow disjoint parts of slices, but its really hard for the borrow checker to know that this is safe -unsafe fn _uncheked_slice(s: &[Q], offset: usize, size: usize) -> &[Q] { - let ptr = s.as_ptr(); - let st = ptr.add(offset); - std::slice::from_raw_parts(st, size) -} - -fn inplace_softmax(x: &mut [Ty]) { - let max_val = x.iter().fold(Ty::NAN, |acc, &v| v.max(acc)); - let mut denom = 0 as Ty; - for v in x.iter_mut() { - *v = (*v - max_val).exp(); - denom += *v; - } - - x.iter_mut().for_each(|v| *v /= denom); -} \ No newline at end of file diff --git a/llamattf/src/stories15M.bin b/llamattf/src/stories15M.bin deleted file mode 100644 index a84ed9f..0000000 Binary files a/llamattf/src/stories15M.bin and /dev/null differ diff --git a/llamattf/src/tokenizer.bin b/llamattf/src/tokenizer.bin deleted file mode 100644 index 18a803f..0000000 Binary files a/llamattf/src/tokenizer.bin and /dev/null differ diff --git a/style/Inter-Regular.woff b/style/Inter-Regular.woff new file mode 100644 index 0000000..749f863 Binary files /dev/null and b/style/Inter-Regular.woff differ diff --git a/style/Inter-SemiBold.woff b/style/Inter-SemiBold.woff new file mode 100644 index 0000000..b804c34 Binary files /dev/null and b/style/Inter-SemiBold.woff differ