Skip to content

Commit

Permalink
Add rubygem (#2)
Browse files Browse the repository at this point in the history
* Adding ruby gem

* Add Instance getter and test

* Typo

* Update comments

* Update README

* Add resize method

* Update comments

* Add header method to patch python lib capability

* Add 4 bytes to account for removing header
  • Loading branch information
tsmartt authored Jul 29, 2021
1 parent 7727193 commit 695f6ac
Show file tree
Hide file tree
Showing 14 changed files with 256 additions and 0 deletions.
8 changes: 8 additions & 0 deletions wasm-thumbnail-rb/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
13 changes: 13 additions & 0 deletions wasm-thumbnail-rb/.rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
AllCops:
TargetRubyVersion: 2.7

Style/StringLiterals:
Enabled: true
EnforcedStyle: double_quotes

Style/StringLiteralsInInterpolation:
Enabled: true
EnforcedStyle: double_quotes

Layout/LineLength:
Max: 120
12 changes: 12 additions & 0 deletions wasm-thumbnail-rb/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

source "https://rubygems.org"

# Specify your gem's dependencies in wasm-thumbnail-rb.gemspec
gemspec

gem "rake", "~> 13.0"

gem "minitest", "~> 5.0"

gem "rubocop", "~> 1.7"
29 changes: 29 additions & 0 deletions wasm-thumbnail-rb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Wasm::Thumbnail::Rb

## Installation

Note this gem has not been released to rubygems. It may in the future but for now it is intended to be referenced via git.

Add this line to your application's Gemfile:

```ruby
gem 'wasm-thumbnail-rb', git: 'gitlocationofgem'
```

And then execute:

$ bundle install

## Usage

See `rb_test.rb` for an example of usage.

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/brave-intl/wasm-thumbnail-rb.
16 changes: 16 additions & 0 deletions wasm-thumbnail-rb/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require "bundler/gem_tasks"
require "rake/testtask"

Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList["test/**/*_test.rb"]
end

require "rubocop/rake_task"

RuboCop::RakeTask.new

task default: %i[test rubocop]
15 changes: 15 additions & 0 deletions wasm-thumbnail-rb/bin/console
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler/setup"
require "wasm/thumbnail/rb"

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start

require "irb"
IRB.start(__FILE__)
8 changes: 8 additions & 0 deletions wasm-thumbnail-rb/bin/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx

bundle install

# Do any other automated setup that you need to do here
94 changes: 94 additions & 0 deletions wasm-thumbnail-rb/lib/wasm/thumbnail/rb.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# frozen_string_literal: true

require_relative "rb/version"
require "wasmer"
module Wasm
module Thumbnail
# As opposed to the PY implementation
module Rb
class Error < StandardError; end

# Now the module is compiled, we can instantiate it. Doing so outside the method where used results in errors.
def self.register_panic(_msg_ptr = nil, _msg_len = nil, _file_ptr = nil, _file_len = nil, _line = nil, _column = nil)
puts("WASM panicked")
end

WASMStore = Wasmer::Store.new
WASMImportObject = Wasmer::ImportObject.new
WASMImportObject.register(
"env",
register_panic: Wasmer::Function.new(
WASMStore,
:register_panic,
Wasmer::FunctionType.new([Wasmer::Type::I32,
Wasmer::Type::I32,
Wasmer::Type::I32,
Wasmer::Type::I32,
Wasmer::Type::I32,
Wasmer::Type::I32], [])
)
)

def self.resize_and_pad_with_header(file_bytes:, width:, height:, size:)
# Let's compile the module to be able to execute it!
wasm_instance = Wasm::Thumbnail::Rb::GetWasmInstance.call

# This tells us how much space we'll need to put our image in the WASM env
image_length = file_bytes.length
input_pointer = wasm_instance.exports.allocate.call(image_length)
# Get a pointer on the allocated memory so we can write to it
memory = wasm_instance.exports.memory.uint8_view input_pointer

# Put the image to resize in the allocated space
(0..image_length - 1).each do |nth|
memory[nth] = file_bytes[nth]
end

# Do the actual resize and pad
# Note that this writes to a portion of memory the new JPEG file, but right pads the rest of the space
# we gave it with 0.
begin
output_pointer = wasm_instance.exports.resize_and_pad.call(input_pointer,
image_length,
width,
height,
size)
rescue RuntimeError
raise "Error processing the image."
end
# Get a pointer to the result
memory = wasm_instance.exports.memory.uint8_view output_pointer

# Only take the buffer that we told the rust function we needed. The resize function
# makes a smaller image than the buffer we said, and then pads out the rest.
bytes = memory.to_a.take(size)

# Deallocate
wasm_instance.exports.deallocate.call(input_pointer, image_length)
wasm_instance.exports.deallocate.call(output_pointer, bytes.length)

bytes
end

def self.resize_and_pad(file_bytes:, width:, height:, size:)
bytes = resize_and_pad_with_header(file_bytes: file_bytes, width: width, height: height, size: size + 4)

# The first 4 bytes are a header until the image. The actual image probably ends well before
# the whole buffer, but we keep the junk data on the end to make all the images the same size
# for privacy concerns.
bytes[4..].pack("C*")
end

# Return an instance so you don't have to constantly compile
class GetWasmInstance
def self.call
# Let's compile the module to be able to execute it!
Wasmer::Instance.new(
Wasmer::Module.new(WASMStore, IO.read("#{__dir__}/rb/data/wasm_thumbnail.wasm", mode: "rb")),
WASMImportObject
)
end
end
end
end
end
Binary file not shown.
9 changes: 9 additions & 0 deletions wasm-thumbnail-rb/lib/wasm/thumbnail/rb/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module Wasm
module Thumbnail
module Rb
VERSION = "0.1.0"
end
end
end
6 changes: 6 additions & 0 deletions wasm-thumbnail-rb/test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
require "wasm/thumbnail/rb"

require "minitest/autorun"
Binary file added wasm-thumbnail-rb/test/wasm/thumbnail/brave.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions wasm-thumbnail-rb/test/wasm/thumbnail/rb_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

require "test_helper"

class Wasm::Thumbnail::RbTest < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::Wasm::Thumbnail::Rb::VERSION
end

def test_it_does_something_useful
file_bytes = File.binread("#{__dir__}/brave.png").unpack("C*")
image = Wasm::Thumbnail::Rb.resize_and_pad(file_bytes: file_bytes,
width: 100,
height: 200,
size: 250_000)
puts "Image resized and padded to size #{image.length}"
end
end
28 changes: 28 additions & 0 deletions wasm-thumbnail-rb/wasm-thumbnail-rb.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

require_relative "lib/wasm/thumbnail/rb/version"

Gem::Specification.new do |spec|
spec.name = "wasm-thumbnail-rb"
spec.version = Wasm::Thumbnail::Rb::VERSION
spec.authors = ["Tyler Smart"]
spec.email = ["[email protected]"]
spec.licenses = ["MPL-2.0"]
spec.summary = "WASM based thumbnail library"
spec.homepage = "https://github.com/brave-intl/wasm-thumbnail"
spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")

spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = "https://github.com/brave-intl/wasm-thumbnail"

# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(File.expand_path(__dir__)) do
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]

spec.add_dependency "wasmer", "~> 1.0"
end

0 comments on commit 695f6ac

Please sign in to comment.