package kvm import ( "encoding/json" "fmt" ) // Constants for input validation const ( // MaxKeyboardKeys defines the maximum number of simultaneous key presses // This matches the USB HID keyboard report specification MaxKeyboardKeys = 6 ) // Input RPC Direct Handlers // This module provides optimized direct handlers for high-frequency input events, // bypassing the reflection-based RPC system for improved performance. // // Performance benefits: // - Eliminates reflection overhead (~2-3ms per call) // - Reduces memory allocations // - Optimizes parameter parsing and validation // - Provides faster code path for input methods // // The handlers maintain full compatibility with existing RPC interface // while providing significant latency improvements for input events. // Ultra-fast input RPC structures for zero-allocation parsing // Bypasses float64 conversion by using typed JSON unmarshaling // InputRPCRequest represents a specialized JSON-RPC request for input methods // This eliminates the map[string]interface{} overhead and float64 conversions type InputRPCRequest struct { JSONRPC string `json:"jsonrpc"` Method string `json:"method"` ID any `json:"id,omitempty"` // Union of all possible input parameters - only relevant fields are populated // Fields ordered for optimal 32-bit alignment: slice pointers, 2-byte fields, 1-byte fields Params struct { // Slice pointers (4 bytes on 32-bit ARM) Keys *[]uint8 `json:"keys,omitempty"` // 2-byte fields grouped together X *uint16 `json:"x,omitempty"` Y *uint16 `json:"y,omitempty"` // 1-byte fields grouped together for optimal packing Modifier *uint8 `json:"modifier,omitempty"` Dx *int8 `json:"dx,omitempty"` Dy *int8 `json:"dy,omitempty"` Buttons *uint8 `json:"buttons,omitempty"` WheelY *int8 `json:"wheelY,omitempty"` } `json:"params,omitempty"` } // Common validation helpers for parameter parsing // These reduce code duplication and provide consistent error messages // Ultra-fast inline validation macros - no function call overhead // These prioritize the happy path (direct int parsing) for maximum performance // validateKeysArray extracts and validates a keys array parameter // Ultra-optimized inline validation for maximum performance func validateKeysArray(params map[string]interface{}, methodName string) ([]uint8, error) { keysInterface, ok := params["keys"].([]interface{}) if !ok { return nil, fmt.Errorf("%s: keys parameter must be an array, got %T", methodName, params["keys"]) } if len(keysInterface) > MaxKeyboardKeys { return nil, fmt.Errorf("%s: too many keys (%d), maximum is %d", methodName, len(keysInterface), MaxKeyboardKeys) } keys := make([]uint8, len(keysInterface)) for i, keyInterface := range keysInterface { // Try int first (most common case for small integers) if intVal, ok := keyInterface.(int); ok { if intVal < 0 || intVal > 255 { return nil, fmt.Errorf("%s: key at index %d value %d out of range [0-255]", methodName, i, intVal) } keys[i] = uint8(intVal) continue } return nil, fmt.Errorf("%s: key at index %d must be a number, got %T", methodName, i, keyInterface) } return keys, nil } // Input parameter structures for direct RPC handlers // These mirror the original RPC method signatures but provide // optimized parsing from JSON map parameters. // KeyboardReportParams represents parameters for keyboard HID report // Matches rpcKeyboardReport(modifier uint8, keys []uint8) type KeyboardReportParams struct { Modifier uint8 `json:"modifier"` // Keyboard modifier keys (Ctrl, Alt, Shift, etc.) Keys []uint8 `json:"keys"` // Array of pressed key codes (up to 6 keys) } // AbsMouseReportParams represents parameters for absolute mouse positioning // Matches rpcAbsMouseReport(x, y uint16, buttons uint8) type AbsMouseReportParams struct { X uint16 `json:"x"` // Absolute X coordinate (0-32767) Y uint16 `json:"y"` // Absolute Y coordinate (0-32767) Buttons uint8 `json:"buttons"` // Mouse button state bitmask } // RelMouseReportParams represents parameters for relative mouse movement // Matches rpcRelMouseReport(dx, dy int8, buttons uint8) type RelMouseReportParams struct { Dx int8 `json:"dx"` // Relative X movement delta (-127 to +127) Dy int8 `json:"dy"` // Relative Y movement delta (-127 to +127) Buttons uint8 `json:"buttons"` // Mouse button state bitmask } // WheelReportParams represents parameters for mouse wheel events // Matches rpcWheelReport(wheelY int8) type WheelReportParams struct { WheelY int8 `json:"wheelY"` // Wheel scroll delta (-127 to +127) } // Ultra-fast typed input handler - completely bypasses float64 conversions // Uses direct JSON unmarshaling to target types for maximum performance func handleInputRPCUltraFast(data []byte) (interface{}, error) { var request InputRPCRequest err := json.Unmarshal(data, &request) if err != nil { return nil, fmt.Errorf("failed to parse input request: %v", err) } switch request.Method { case "keyboardReport": if request.Params.Modifier == nil || request.Params.Keys == nil { return nil, fmt.Errorf("keyboardReport: missing required parameters") } keys := *request.Params.Keys if len(keys) > MaxKeyboardKeys { return nil, fmt.Errorf("keyboardReport: too many keys (max %d)", MaxKeyboardKeys) } _, err = rpcKeyboardReport(*request.Params.Modifier, keys) return nil, err case "absMouseReport": if request.Params.X == nil || request.Params.Y == nil || request.Params.Buttons == nil { return nil, fmt.Errorf("absMouseReport: missing required parameters") } x, y, buttons := *request.Params.X, *request.Params.Y, *request.Params.Buttons if x > 32767 || y > 32767 { return nil, fmt.Errorf("absMouseReport: coordinates out of range") } return nil, rpcAbsMouseReport(x, y, buttons) case "relMouseReport": if request.Params.Dx == nil || request.Params.Dy == nil || request.Params.Buttons == nil { return nil, fmt.Errorf("relMouseReport: missing required parameters") } return nil, rpcRelMouseReport(*request.Params.Dx, *request.Params.Dy, *request.Params.Buttons) case "wheelReport": if request.Params.WheelY == nil { return nil, fmt.Errorf("wheelReport: missing wheelY parameter") } return nil, rpcWheelReport(*request.Params.WheelY) default: return nil, fmt.Errorf("unknown input method: %s", request.Method) } } // Direct handler for keyboard reports // Ultra-optimized path with inlined validation for maximum performance func handleKeyboardReportDirect(params map[string]interface{}) (interface{}, error) { // Inline modifier validation - prioritize int path var modifier uint8 if intVal, ok := params["modifier"].(int); ok { if intVal < 0 || intVal > 255 { return nil, fmt.Errorf("keyboardReport: modifier value %d out of range [0-255]", intVal) } modifier = uint8(intVal) } else if floatVal, ok := params["modifier"].(float64); ok { if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 255 { return nil, fmt.Errorf("keyboardReport: modifier value %v invalid", floatVal) } modifier = uint8(floatVal) } else { return nil, fmt.Errorf("keyboardReport: modifier must be a number") } // Extract and validate keys array keys, err := validateKeysArray(params, "keyboardReport") if err != nil { return nil, err } _, err = rpcKeyboardReport(modifier, keys) return nil, err } // Direct handler for absolute mouse reports // Ultra-optimized path with inlined validation for maximum performance func handleAbsMouseReportDirect(params map[string]interface{}) (interface{}, error) { // Inline x coordinate validation - check float64 first (most common JSON number type) var x uint16 if floatVal, ok := params["x"].(float64); ok { if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 32767 { return nil, fmt.Errorf("absMouseReport: x value %v invalid", floatVal) } x = uint16(floatVal) } else if intVal, ok := params["x"].(int); ok { if intVal < 0 || intVal > 32767 { return nil, fmt.Errorf("absMouseReport: x value %d out of range [0-32767]", intVal) } x = uint16(intVal) } else { return nil, fmt.Errorf("absMouseReport: x must be a number") } // Inline y coordinate validation - check float64 first (most common JSON number type) var y uint16 if floatVal, ok := params["y"].(float64); ok { if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 32767 { return nil, fmt.Errorf("absMouseReport: y value %v invalid", floatVal) } y = uint16(floatVal) } else if intVal, ok := params["y"].(int); ok { if intVal < 0 || intVal > 32767 { return nil, fmt.Errorf("absMouseReport: y value %d out of range [0-32767]", intVal) } y = uint16(intVal) } else { return nil, fmt.Errorf("absMouseReport: y must be a number") } // Inline buttons validation var buttons uint8 if intVal, ok := params["buttons"].(int); ok { if intVal < 0 || intVal > 255 { return nil, fmt.Errorf("absMouseReport: buttons value %d out of range [0-255]", intVal) } buttons = uint8(intVal) } else if floatVal, ok := params["buttons"].(float64); ok { if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 255 { return nil, fmt.Errorf("absMouseReport: buttons value %v invalid", floatVal) } buttons = uint8(floatVal) } else { return nil, fmt.Errorf("absMouseReport: buttons must be a number") } return nil, rpcAbsMouseReport(x, y, buttons) } // Direct handler for relative mouse reports // Ultra-optimized path with inlined validation for maximum performance func handleRelMouseReportDirect(params map[string]interface{}) (interface{}, error) { // Inline dx validation - check float64 first (most common JSON number type) var dx int8 if floatVal, ok := params["dx"].(float64); ok { if floatVal != float64(int(floatVal)) || floatVal < -128 || floatVal > 127 { return nil, fmt.Errorf("relMouseReport: dx value %v invalid", floatVal) } dx = int8(floatVal) } else if intVal, ok := params["dx"].(int); ok { if intVal < -128 || intVal > 127 { return nil, fmt.Errorf("relMouseReport: dx value %d out of range [-128 to 127]", intVal) } dx = int8(intVal) } else { return nil, fmt.Errorf("relMouseReport: dx must be a number") } // Inline dy validation - check float64 first (most common JSON number type) var dy int8 if floatVal, ok := params["dy"].(float64); ok { if floatVal != float64(int(floatVal)) || floatVal < -128 || floatVal > 127 { return nil, fmt.Errorf("relMouseReport: dy value %v invalid", floatVal) } dy = int8(floatVal) } else if intVal, ok := params["dy"].(int); ok { if intVal < -128 || intVal > 127 { return nil, fmt.Errorf("relMouseReport: dy value %d out of range [-128 to 127]", intVal) } dy = int8(intVal) } else { return nil, fmt.Errorf("relMouseReport: dy must be a number") } // Inline buttons validation - check float64 first (most common JSON number type) var buttons uint8 if floatVal, ok := params["buttons"].(float64); ok { if floatVal != float64(int(floatVal)) || floatVal < 0 || floatVal > 255 { return nil, fmt.Errorf("relMouseReport: buttons value %v invalid", floatVal) } buttons = uint8(floatVal) } else if intVal, ok := params["buttons"].(int); ok { if intVal < 0 || intVal > 255 { return nil, fmt.Errorf("relMouseReport: buttons value %d out of range [0-255]", intVal) } buttons = uint8(intVal) } else { return nil, fmt.Errorf("relMouseReport: buttons must be a number") } return nil, rpcRelMouseReport(dx, dy, buttons) } // Direct handler for wheel reports // Ultra-optimized path with inlined validation for maximum performance func handleWheelReportDirect(params map[string]interface{}) (interface{}, error) { // Inline wheelY validation var wheelY int8 if intVal, ok := params["wheelY"].(int); ok { if intVal < -128 || intVal > 127 { return nil, fmt.Errorf("wheelReport: wheelY value %d out of range [-128 to 127]", intVal) } wheelY = int8(intVal) } else if floatVal, ok := params["wheelY"].(float64); ok { if floatVal != float64(int(floatVal)) || floatVal < -128 || floatVal > 127 { return nil, fmt.Errorf("wheelReport: wheelY value %v invalid", floatVal) } wheelY = int8(floatVal) } else { return nil, fmt.Errorf("wheelReport: wheelY must be a number") } return nil, rpcWheelReport(wheelY) } // handleInputRPCDirect routes input method calls to their optimized direct handlers // This is the main entry point for the fast path that bypasses reflection. // It provides significant performance improvements for high-frequency input events. // // Performance monitoring: Consider adding metrics collection here to track // latency improvements and call frequency for production monitoring. func handleInputRPCDirect(method string, params map[string]interface{}) (interface{}, error) { switch method { case "keyboardReport": return handleKeyboardReportDirect(params) case "absMouseReport": return handleAbsMouseReportDirect(params) case "relMouseReport": return handleRelMouseReportDirect(params) case "wheelReport": return handleWheelReportDirect(params) default: // This should never happen if isInputMethod is correctly implemented return nil, fmt.Errorf("handleInputRPCDirect: unsupported method '%s'", method) } } // isInputMethod determines if a given RPC method should use the optimized direct path // Returns true for input-related methods that have direct handlers implemented. // This function must be kept in sync with handleInputRPCDirect. func isInputMethod(method string) bool { switch method { case "keyboardReport", "absMouseReport", "relMouseReport", "wheelReport": return true default: return false } }