dispatch.go raw

   1  package organ
   2  
   3  import (
   4  	"fmt"
   5  )
   6  
   7  // HostFunc is a function that performs the same operation as an organ,
   8  // implemented in host-side Go. This is the fallback when no organ is
   9  // available or all organs fail.
  10  type HostFunc func(input []byte) ([]byte, error)
  11  
  12  // Dispatcher routes operations to the best available organ, falling
  13  // back to host-side code if no organ is loaded or all organs fail.
  14  //
  15  // The fallback hierarchy:
  16  //  1. Best WASM organ of the type (highest fitness)
  17  //  2. Second-best WASM organ (if any)
  18  //  3. Host-side Go implementation (always available)
  19  type Dispatcher struct {
  20  	Registry *Registry
  21  	Type     OrganType
  22  	Opcode   byte
  23  	Host     HostFunc // always-available fallback
  24  }
  25  
  26  // Dispatch sends input through the fallback hierarchy.
  27  // Returns the result and which tier produced it (0 = best organ,
  28  // 1 = second-best organ, 2 = host fallback).
  29  func (d *Dispatcher) Dispatch(input []byte) (result []byte, tier int, err error) {
  30  	if d.Registry != nil {
  31  		organs := d.Registry.All(d.Type)
  32  		for i, org := range organs {
  33  			result, err = d.Registry.Call(org.Manifest.ID, d.Opcode, input)
  34  			if err == nil {
  35  				return result, i, nil
  36  			}
  37  			// Organ failed — try next.
  38  		}
  39  	}
  40  
  41  	// All organs failed or none loaded — use host fallback.
  42  	if d.Host == nil {
  43  		return nil, -1, fmt.Errorf("no organ or host fallback for %s", d.Type)
  44  	}
  45  
  46  	result, err = d.Host(input)
  47  	if err != nil {
  48  		return nil, -1, fmt.Errorf("host fallback: %w", err)
  49  	}
  50  
  51  	tier = 2
  52  	if d.Registry != nil && len(d.Registry.All(d.Type)) > 0 {
  53  		// Organs existed but all failed.
  54  		tier = len(d.Registry.All(d.Type))
  55  	}
  56  
  57  	return result, tier, nil
  58  }
  59  
  60  // CanDispatch checks whether any organ can handle this input.
  61  // Uses the CanDigest opcode (0x01) for enzyme organs.
  62  // Returns true if any organ or host reports capability.
  63  func (d *Dispatcher) CanDispatch(input []byte) bool {
  64  	if d.Registry != nil {
  65  		organs := d.Registry.All(d.Type)
  66  		for _, org := range organs {
  67  			result, err := d.Registry.Call(org.Manifest.ID, OpCanDigest, input)
  68  			if err == nil && len(result) > 0 && result[0] == 0x01 {
  69  				return true
  70  			}
  71  		}
  72  	}
  73  	// No organ can handle it — host decides.
  74  	return d.Host != nil
  75  }
  76