2026-04-15 19:20:27 +03:00
|
|
|
package model
|
2026-04-15 19:09:21 +03:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type BenchmarkConfig struct {
|
|
|
|
|
DurationSecs int `json:"durationSecs"`
|
|
|
|
|
Intensity int `json:"intensity"`
|
|
|
|
|
CoreFilter int `json:"coreFilter"`
|
|
|
|
|
MultiCore bool `json:"multiCore"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type CPUInfo struct {
|
|
|
|
|
BrandString string `json:"brandString"`
|
|
|
|
|
VendorID string `json:"vendorID"`
|
|
|
|
|
PhysicalCores int `json:"physicalCores"`
|
|
|
|
|
LogicalCores int `json:"logicalCores"`
|
|
|
|
|
BaseClockMHz int `json:"baseClockMHz"`
|
|
|
|
|
BoostClockMHz int `json:"boostClockMHz"`
|
|
|
|
|
L1DataKB int `json:"l1DataKB"`
|
|
|
|
|
L2KB int `json:"l2KB"`
|
|
|
|
|
L3MB int `json:"l3MB"`
|
|
|
|
|
IsHybrid bool `json:"isHybrid,omitempty"`
|
|
|
|
|
Has3DVCache bool `json:"has3DVCache,omitempty"`
|
|
|
|
|
PCoreCount int `json:"pCoreCount,omitempty"`
|
|
|
|
|
ECoreCount int `json:"eCoreCount,omitempty"`
|
|
|
|
|
Cores []CPUCoreDescriptor `json:"cores,omitempty"`
|
|
|
|
|
SupportedFeatures []string `json:"supportedFeatures,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type CPUCoreDescriptor struct {
|
|
|
|
|
LogicalID int `json:"LogicalID"`
|
|
|
|
|
PhysicalID int `json:"PhysicalID"`
|
|
|
|
|
CoreID int `json:"CoreID"`
|
|
|
|
|
Type int `json:"Type"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-17 13:57:55 +03:00
|
|
|
type SystemInfo struct {
|
|
|
|
|
Hostname string `json:"hostname,omitempty"`
|
|
|
|
|
OSName string `json:"osName,omitempty"`
|
|
|
|
|
OSVersion string `json:"osVersion,omitempty"`
|
|
|
|
|
Distro string `json:"distro,omitempty"`
|
|
|
|
|
KernelVersion string `json:"kernelVersion,omitempty"`
|
|
|
|
|
KernelArch string `json:"kernelArch,omitempty"`
|
|
|
|
|
Architecture string `json:"architecture,omitempty"`
|
|
|
|
|
Locale string `json:"locale,omitempty"`
|
|
|
|
|
Timezone string `json:"timezone,omitempty"`
|
|
|
|
|
Region string `json:"region,omitempty"`
|
|
|
|
|
Country string `json:"country,omitempty"`
|
|
|
|
|
City string `json:"city,omitempty"`
|
|
|
|
|
ISP string `json:"isp,omitempty"`
|
|
|
|
|
SessionID string `json:"sessionID,omitempty"`
|
|
|
|
|
UserID string `json:"userID,omitempty"`
|
|
|
|
|
ClientVersion string `json:"clientVersion,omitempty"`
|
|
|
|
|
AppVersion string `json:"appVersion,omitempty"`
|
|
|
|
|
IPAddress string `json:"ipAddress,omitempty"`
|
|
|
|
|
ForwardedFor string `json:"forwardedFor,omitempty"`
|
|
|
|
|
UserAgent string `json:"userAgent,omitempty"`
|
|
|
|
|
Extra map[string]any `json:"extra,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 19:09:21 +03:00
|
|
|
type CoreResult struct {
|
|
|
|
|
LogicalID int `json:"logicalID"`
|
|
|
|
|
CoreType string `json:"coreType"`
|
|
|
|
|
MOpsPerSec float64 `json:"mOpsPerSec"`
|
|
|
|
|
TotalOps int64 `json:"totalOps"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type BenchmarkResult struct {
|
|
|
|
|
Config BenchmarkConfig `json:"config"`
|
|
|
|
|
CPUInfo CPUInfo `json:"cpuInfo"`
|
|
|
|
|
StartedAt time.Time `json:"startedAt"`
|
|
|
|
|
Duration int64 `json:"duration"`
|
|
|
|
|
TotalOps int64 `json:"totalOps"`
|
|
|
|
|
MOpsPerSec float64 `json:"mOpsPerSec"`
|
|
|
|
|
Score int64 `json:"score"`
|
|
|
|
|
CoreResults []CoreResult `json:"coreResults"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Submission struct {
|
2026-04-17 13:57:55 +03:00
|
|
|
SubmissionID string `json:"submissionID"`
|
|
|
|
|
Submitter string `json:"submitter"`
|
|
|
|
|
Platform string `json:"platform"`
|
|
|
|
|
SubmittedAt time.Time `json:"submittedAt"`
|
|
|
|
|
SystemInfo *SystemInfo `json:"systemInfo,omitempty"`
|
2026-04-15 19:09:21 +03:00
|
|
|
BenchmarkResult
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b BenchmarkResult) Validate() error {
|
|
|
|
|
if strings.TrimSpace(b.CPUInfo.BrandString) == "" {
|
|
|
|
|
return errors.New("cpuInfo.brandString is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if b.StartedAt.IsZero() {
|
|
|
|
|
return errors.New("startedAt is required and must be RFC3339-compatible")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if b.Config.DurationSecs <= 0 {
|
|
|
|
|
return errors.New("config.durationSecs must be greater than zero")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if b.Duration <= 0 {
|
|
|
|
|
return errors.New("duration must be greater than zero")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if b.TotalOps < 0 || b.Score < 0 || b.MOpsPerSec < 0 {
|
|
|
|
|
return errors.New("duration, totalOps, mOpsPerSec, and score must be non-negative")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if b.CPUInfo.LogicalCores < 0 || b.CPUInfo.PhysicalCores < 0 {
|
|
|
|
|
return errors.New("cpu core counts must be non-negative")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, result := range b.CoreResults {
|
|
|
|
|
if result.LogicalID < 0 {
|
|
|
|
|
return fmt.Errorf("coreResults.logicalID must be non-negative")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if result.MOpsPerSec < 0 || result.TotalOps < 0 {
|
|
|
|
|
return fmt.Errorf("coreResults values must be non-negative")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 19:20:27 +03:00
|
|
|
func NormalizeSubmitter(submitter string) string {
|
2026-04-15 19:09:21 +03:00
|
|
|
submitter = strings.TrimSpace(submitter)
|
|
|
|
|
if submitter == "" {
|
|
|
|
|
return "Anonymous"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return submitter
|
|
|
|
|
}
|
|
|
|
|
|
feat(search): support platform and benchmark config filters
Add platform handling to submissions and persist a normalized value (`windows`, `linux`, `macos`) with a default of `windows` when omitted.
Extend search/index filtering to support `thread`, `platform`, `intensity`, and `durationSecs` alongside existing text/CPU token matching, and wire these params through request parsing, page data, and navigation URLs.
Update API/README docs and examples to reflect the new submission inputs and search capabilities so users can run more precise queries.feat(search): support platform and benchmark config filters
Add platform handling to submissions and persist a normalized value (`windows`, `linux`, `macos`) with a default of `windows` when omitted.
Extend search/index filtering to support `thread`, `platform`, `intensity`, and `durationSecs` alongside existing text/CPU token matching, and wire these params through request parsing, page data, and navigation URLs.
Update API/README docs and examples to reflect the new submission inputs and search capabilities so users can run more precise queries.
2026-04-15 20:23:37 +03:00
|
|
|
func NormalizePlatform(platform string) string {
|
|
|
|
|
switch strings.ToLower(strings.TrimSpace(platform)) {
|
|
|
|
|
case "", "windows":
|
|
|
|
|
return "windows"
|
|
|
|
|
case "linux":
|
|
|
|
|
return "linux"
|
|
|
|
|
case "macos":
|
|
|
|
|
return "macos"
|
|
|
|
|
default:
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ValidatePlatform(platform string) error {
|
|
|
|
|
if NormalizePlatform(platform) == "" {
|
|
|
|
|
return errors.New("platform must be one of windows, linux, or macos")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 19:20:27 +03:00
|
|
|
func ThreadModeLabel(multiCore bool) string {
|
2026-04-15 19:09:21 +03:00
|
|
|
if multiCore {
|
|
|
|
|
return "Multi-threaded"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "Single-threaded"
|
|
|
|
|
}
|
2026-04-15 19:20:27 +03:00
|
|
|
|
|
|
|
|
func CloneSubmission(submission *Submission) *Submission {
|
|
|
|
|
if submission == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copySubmission := *submission
|
|
|
|
|
if len(submission.CoreResults) > 0 {
|
|
|
|
|
copySubmission.CoreResults = append([]CoreResult(nil), submission.CoreResults...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(submission.CPUInfo.Cores) > 0 {
|
|
|
|
|
copySubmission.CPUInfo.Cores = append([]CPUCoreDescriptor(nil), submission.CPUInfo.Cores...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(submission.CPUInfo.SupportedFeatures) > 0 {
|
|
|
|
|
copySubmission.CPUInfo.SupportedFeatures = append([]string(nil), submission.CPUInfo.SupportedFeatures...)
|
|
|
|
|
}
|
2026-04-17 13:57:55 +03:00
|
|
|
if submission.SystemInfo != nil {
|
|
|
|
|
copySystemInfo := *submission.SystemInfo
|
|
|
|
|
if len(submission.SystemInfo.Extra) > 0 {
|
|
|
|
|
copySystemInfo.Extra = make(map[string]any, len(submission.SystemInfo.Extra))
|
|
|
|
|
for key, value := range submission.SystemInfo.Extra {
|
|
|
|
|
copySystemInfo.Extra[key] = value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
copySubmission.SystemInfo = ©SystemInfo
|
|
|
|
|
}
|
2026-04-15 19:20:27 +03:00
|
|
|
|
|
|
|
|
return ©Submission
|
|
|
|
|
}
|