Skip to content

Commit

Permalink
Use new sum method on spans to better avoid collisions (#68)
Browse files Browse the repository at this point in the history
* Add `ReadOnlySpan<byte>.MSum()`

* Irc: Changed internal tag values to match `ReadOnlySpan<byte>.MSum()` rather than `.Sum()`

* PubSub: Switch to MSum()

* Add some MSum() tests for irc

* Sum tests for pubsub

* remove obsolete test

* use actual MSum()
  • Loading branch information
occluder authored Jan 6, 2024
1 parent 1af7e84 commit d078b8f
Show file tree
Hide file tree
Showing 20 changed files with 373 additions and 190 deletions.
6 changes: 5 additions & 1 deletion MiniTwitch.Common/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@

### Minor changes

- Improved handling of WebSocket reads
- Improved handling of WebSocket reads

### Dev

- Added `ReadOnlySpan<byte>.MSum()` extension, which returns sum * first value
17 changes: 17 additions & 0 deletions MiniTwitch.Common/Extensions/SpanExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@ public static int Sum(this ReadOnlySpan<byte> source)
return sum;
}

public static int MSum(this ReadOnlySpan<byte> source)
{
if (source.Length == 0)
{
return 0;
}

int m = source[0];
int sum = 0;
foreach (byte b in source)
{
sum += b;
}

return m * sum;
}

public static int CopyUnescaped(this ReadOnlySpan<byte> source, Span<byte> destination)
{
const byte backSlash = (byte)'\\';
Expand Down
62 changes: 1 addition & 61 deletions MiniTwitch.Irc.Test/PrivmsgTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Drawing;
using System.Globalization;
using MiniTwitch.Irc.Enums;
using MiniTwitch.Irc.Enums;
using MiniTwitch.Irc.Models;
using Xunit;

Expand Down Expand Up @@ -184,64 +182,6 @@ public void WithNestedReply()
Assert.Equal(1687870580760, privmsg.TmiSentTs);
}

[Fact]
public void WithHypeChat()
{
var privmsg = Privmsg.Construct("@badge-info=subscriber/28;badges=subscriber/24,chatter-cs-go-2022/1;color=#00FFFF;display-name=MoodyJ1;emotes=;first-msg=0;flags=;id=ad4517d0-97b7-4ca1-91eb-f43edf3bf9b3;mod=0;pinned-chat-paid-amount=100;pinned-chat-paid-canonical-amount=100;pinned-chat-paid-currency=GBP;pinned-chat-paid-exponent=2;pinned-chat-paid-is-system-message=0;pinned-chat-paid-level=ONE;returning-chatter=0;room-id=22484632;subscriber=1;tmi-sent-ts=1687470141956;turbo=0;user-id=246651933;user-type= :[email protected] PRIVMSG #forsen :f o r s e n");

// Author
Assert.Equal(246651933, privmsg.Author.Id);
Assert.Equal(UserType.None, privmsg.Author.Type);
Assert.True(privmsg.Author.IsSubscriber);
Assert.Equal("MoodyJ1", privmsg.Author.DisplayName);
Assert.Equal("moodyj1", privmsg.Author.Name);
Assert.False(privmsg.Author.IsMod);
Assert.False(privmsg.Author.IsTurbo);
Assert.Equal(Color.FromArgb(int.Parse("00FFFF", NumberStyles.HexNumber)).Name, privmsg.Author.ChatColor.Name);
Assert.Equal("subscriber/28", privmsg.Author.BadgeInfo);
Assert.Equal("subscriber/24,chatter-cs-go-2022/1", privmsg.Author.Badges);
Assert.False(privmsg.Author.IsVip);

// No reply
Assert.False(privmsg.Reply.HasContent);
Assert.Equal(string.Empty, privmsg.Reply.ParentMessage);
Assert.Equal(string.Empty, privmsg.Reply.ParentDisplayName);
Assert.Equal(string.Empty, privmsg.Reply.ParentUsername);
Assert.Equal(string.Empty, privmsg.Reply.ParentMessageId);
Assert.Equal(0, privmsg.Reply.ParentUserId);
Assert.Equal(string.Empty, privmsg.Reply.ParentThreadMessageId);
Assert.Equal(string.Empty, privmsg.Reply.ParentThreadUsername);

// Is hypechat message
Assert.True(privmsg.HypeChat.HasContent);
Assert.Equal(HypeChatLevel.ONE, privmsg.HypeChat.Level);
Assert.Equal(CurrencyCode.GBP, privmsg.HypeChat.Currency);
Assert.Equal(2, privmsg.HypeChat.Exponent);
Assert.Equal(100, privmsg.HypeChat.PaidAmount);
Assert.False(privmsg.HypeChat.IsSystemMessage);
Assert.Equal(1, privmsg.HypeChat.GetActualAmount());
Assert.Equal(TimeSpan.FromSeconds(30), privmsg.HypeChat.GetPinDuration());

// Channel
Assert.Equal("forsen", privmsg.Channel.Name);
Assert.Equal(22484632, privmsg.Channel.Id);

// No source
Assert.Null(privmsg.Source);
Assert.True(privmsg.ReplyWith(default!, default!).IsCompleted);

Assert.Equal(0, privmsg.Bits);
Assert.Equal("f o r s e n", privmsg.Content);
Assert.Equal(string.Empty, privmsg.Emotes);
Assert.Equal(string.Empty, privmsg.Flags);
Assert.Equal("ad4517d0-97b7-4ca1-91eb-f43edf3bf9b3", privmsg.Id);
Assert.False(privmsg.IsAction);
Assert.False(privmsg.IsFirstMessage);
Assert.False(privmsg.IsReturningChatter);
Assert.Equal(string.Empty, privmsg.Nonce);
Assert.Equal(1687470141956, privmsg.TmiSentTs);
}

[Fact]
public void WithBits()
{
Expand Down
118 changes: 118 additions & 0 deletions MiniTwitch.Irc.Test/SpanSumTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using System.Text;
using MiniTwitch.Common.Extensions;
using MiniTwitch.Irc.Internal.Enums;
using Xunit;
using Xunit.Sdk;

namespace MiniTwitch.Irc.Test;
public class SpanSumTests
{
// <expected, string>
static readonly Dictionary<int, string> _values = new()
{
{ (int)Tags.Id, "id" },
{ (int)Tags.Mod, "mod" },
{ (int)Tags.Vip, "vip" },
{ (int)Tags.R9K, "r9k" },
{ (int)Tags.Bits, "bits" },
{ (int)Tags.Flags, "flags" },
{ (int)Tags.Color, "color" },
{ (int)Tags.Slow, "slow" },
{ (int)Tags.Turbo, "turbo" },
{ (int)Tags.Badges, "badges" },
{ (int)Tags.Emotes, "emotes" },
{ (int)Tags.UserId, "user-id" },
{ (int)Tags.FirstMsg, "first-msg" },
{ (int)Tags.MsgId, "msg-id" },
{ (int)Tags.Login, "login" },
{ (int)Tags.RoomId, "room-id" },
{ (int)Tags.SubsOnly, "subs-only" },
{ (int)Tags.EmoteOnly, "emote-only" },
{ (int)Tags.TmiSentTs, "tmi-sent-ts" },
{ (int)Tags.BanDuration, "ban-duration" },
{ (int)Tags.TargetMsgId, "target-msg-id" },
{ (int)Tags.TargetUserId, "target-user-id" },
{ (int)Tags.FollowersOnly, "followers-only" },
{ (int)Tags.UserType, "user-type" },
{ (int)Tags.BadgeInfo, "badge-info" },
{ (int)Tags.Subscriber, "subscriber" },
{ (int)Tags.ClientNonce, "client-nonce" },
{ (int)Tags.DisplayName, "display-name" },
{ (int)Tags.ReturningChatter, "returning-chatter" },
{ (int)Tags.ReplyParentMsgId, "reply-parent-msg-id" },
{ (int)Tags.ReplyParentUserId, "reply-parent-user-id" },
{ (int)Tags.ReplyParentMsgBody, "reply-parent-msg-body" },
{ (int)Tags.ReplyParentUserLogin, "reply-parent-user-login" },
{ (int)Tags.ReplyParentDisplayName, "reply-parent-display-name" },
{ (int)Tags.ReplyThreadParentMsgId, "reply-thread-parent-msg-id" },
{ (int)Tags.ReplyThreadParentUserLogin, "reply-thread-parent-user-login" },
{ (int)Tags.SystemMsg, "system-msg" },
{ (int)Tags.MsgParamColor, "msg-param-color" },
{ (int)Tags.MsgParamMonths, "msg-param-months" },
{ (int)Tags.MsgParamSubPlan, "msg-param-sub-plan" },
{ (int)Tags.MsgParamSenderName, "msg-param-sender-name" },
{ (int)Tags.MsgParamGiftMonths, "msg-param-gift-months" },
{ (int)Tags.MsgParamViewerCount, "msg-param-viewerCount" },
{ (int)Tags.MsgParamRecipientId, "msg-param-recipient-id" },
{ (int)Tags.MsgParamSenderLogin, "msg-param-sender-login" },
{ (int)Tags.MsgParamSenderCount, "msg-param-sender-count" },
{ (int)Tags.MsgParamSubPlanName, "msg-param-sub-plan-name" },
{ (int)Tags.MsgParamStreakMonths, "msg-param-streak-months" },
{ (int)Tags.MsgParamMassGiftCount, "msg-param-mass-gift-count" },
{ (int)Tags.MsgParamCumulativeMonths, "msg-param-cumulative-months" },
{ (int)Tags.MsgParamRecipientUserName, "msg-param-recipient-user-name" },
{ (int)Tags.MsgParamShouldShareStreak, "msg-param-should-share-streak" },
{ (int)Tags.MsgParamRecipientDisplayName, "msg-param-recipient-display-name" },
{ (int)Tags.MsgParamCharityName, "msg-param-charity-name" },
{ (int)Tags.MsgParamDonationAmount, "msg-param-donation-amount" },
{ (int)Tags.MsgParamExponent, "msg-param-exponent" },
{ (int)Tags.MsgParamDonationCurrency, "msg-param-donation-currency" },
{ (int)Tags.EmoteSets, "emote-sets" },
{ (int)Tags.MessageId, "message-id" },
{ (int)Tags.ThreadId, "thread-id" },
};

[Fact]
public void Test_Includes_All_Tags()
{
var tags = Enum.GetNames(typeof(Tags));
if (tags.Length != _values.Count)
{
var notIncluded = tags.Where(x => !_values.ContainsKey((int)Enum.Parse<Tags>(x)));
Assert.Fail($"Test does not account for all tags, expected {tags.Length}, got {_values.Count}." +
$"\nMissing tags: \n{string.Join("\n", notIncluded)}");
}

foreach (var tag in tags)
{
var enumValue = Enum.Parse<Tags>(tag);
try
{
Assert.True(_values.ContainsKey((int)enumValue));
}
catch (TrueException)
{
Assert.Fail($"Enum value {enumValue} not present.");
}
}
}

[Fact]
public void All_Tag_Values_Match_Sum()
{
foreach (var kvp in _values)
{
if (kvp.Key != MSum(kvp.Value))
{
Assert.Fail($"Tag \"{kvp.Value}\" does not match expected sum {kvp.Key}. Got {MSum(kvp.Value)} instead.");
}
}
}


private static int MSum(string s)
{
var source = Encoding.UTF8.GetBytes(s);
return SpanExtensions.MSum(source);
}
}
7 changes: 6 additions & 1 deletion MiniTwitch.Irc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@

### Minor changes

- Updated logging strings for joining suspended/renamed/disabled/deleted channels
- Updated logging strings for joining suspended/renamed/disabled/deleted channels

### Dev

- Changed internal tag values to match `ReadOnlySpan<byte>.MSum()` rather than `.Sum()`. Usages of the latter were also switched
- Tag values of Hype Chat have been removed
125 changes: 60 additions & 65 deletions MiniTwitch.Irc/Internal/Enums/Tags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,64 @@

internal enum Tags
{
Id = 205,
R9K = 278,
Mod = 320,
Vip = 335,
Bits = 434,
Slow = 453,
Flags = 525,
Login = 537,
Color = 543,
Turbo = 556,
MsgId = 577,
Badges = 614,
Emotes = 653,
RoomId = 695,
UserId = 697,
ThreadId = 882,
FirstMsg = 924,
SubsOnly = 940,
UserType = 942,
BadgeInfo = 972,
MessageId = 991,
EmoteSets = 1030,
EmoteOnly = 1033,
SystemMsg = 1049,
Subscriber = 1076,
TmiSentTs = 1093,
ClientNonce = 1215,
BanDuration = 1220,
DisplayName = 1220,
TargetMsgId = 1269,
TargetUserId = 1389,
FollowersOnly = 1484,
MsgParamColor = 1489,
MsgParamMonths = 1611,
MsgParamSubPlan = 1748,
ReturningChatter = 1782,
MsgParamExponent = 1827,
ReplyParentMsgId = 1873,
ReplyParentUserId = 1993,
MsgParamSenderName = 2049,
MsgParamGiftMonths = 2082,
ReplyParentMsgBody = 2098,
MsgParamViewerCount = 2125,
PinnedChatPaidLevel = 2139,
MsgParamRecipientId = 2159,
MsgParamCharityName = 2164,
MsgParamSenderLogin = 2169,
MsgParamSenderCount = 2185,
MsgParamSubPlanName = 2210,
PinnedChatPaidAmount = 2263,
MsgParamStreakMonths = 2306,
ReplyParentUserLogin = 2325,
MsgParamMassGiftCount = 2451,
PinnedChatPaidCurrency = 2478,
PinnedChatPaidExponent = 2484,
MsgParamDonationAmount = 2511,
ReplyParentDisplayName = 2516,
ReplyThreadParentMsgId = 2550,
MsgParamDonationCurrency = 2726,
MsgParamCumulativeMonths = 2743,
MsgParamRecipientUserName = 2863,
MsgParamShouldShareStreak = 2872,
ReplyThreadParentUserLogin = 3002,
MsgparamRecipientDisplayName = 3174,
PinnedChatPaidIsSystemMessage = 3331,
Id = 21525,
Mod = 34880,
Vip = 39530,
R9K = 31692,
Bits = 42532,
Flags = 53550,
Color = 53757,
Slow = 52095,
Turbo = 64496,
Badges = 60172,
Emotes = 65953,
UserId = 81549,
FirstMsg = 94248,
MsgId = 62893,
Login = 57996,
RoomId = 79230,
SubsOnly = 108100,
EmoteOnly = 104333,
TmiSentTs = 126788,
BanDuration = 119560,
TargetMsgId = 147204,
TargetUserId = 161124,
FollowersOnly = 151368,
UserType = 110214,
BadgeInfo = 95256,
Subscriber = 123740,
ClientNonce = 120285,
DisplayName = 122000,
ReturningChatter = 203148,
ReplyParentMsgId = 213522,
ReplyParentUserId = 227202,
ReplyParentMsgBody = 239172,
ReplyParentUserLogin = 265050,
ReplyParentDisplayName = 286824,
ReplyThreadParentMsgId = 290700,
ReplyThreadParentUserLogin = 342228,
SystemMsg = 120635,
MsgParamColor = 162301,
MsgParamMonths = 175599,
MsgParamSubPlan = 190532,
MsgParamSenderName = 223341,
MsgParamGiftMonths = 226938,
MsgParamViewerCount = 231625,
MsgParamRecipientId = 235331,
MsgParamSenderLogin = 236421,
MsgParamSenderCount = 238165,
MsgParamSubPlanName = 240890,
MsgParamStreakMonths = 251354,
MsgParamMassGiftCount = 267159,
MsgParamCumulativeMonths = 298987,
MsgParamRecipientUserName = 312067,
MsgParamShouldShareStreak = 313048,
MsgParamRecipientDisplayName = 345966,
MsgParamCharityName = 235876,
MsgParamDonationAmount = 273699,
MsgParamExponent = 199143,
MsgParamDonationCurrency = 297134,
EmoteSets = 104030,
MessageId = 108019,
ThreadId = 102312,
}
2 changes: 1 addition & 1 deletion MiniTwitch.Irc/Models/ClearChat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ internal Clearchat(ref IrcMessage message)
ReadOnlySpan<byte> tagKey = tag.Key.Span;
ReadOnlySpan<byte> tagValue = tag.Value.Span;

switch (tagKey.Sum())
switch (tagKey.MSum())
{
//room-id
case (int)Tags.RoomId:
Expand Down
2 changes: 1 addition & 1 deletion MiniTwitch.Irc/Models/Clearmsg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ internal Clearmsg(ref IrcMessage message)
ReadOnlySpan<byte> tagKey = tag.Key.Span;
ReadOnlySpan<byte> tagValue = tag.Value.Span;

switch (tagKey.Sum())
switch (tagKey.MSum())
{
//login
case (int)Tags.Login:
Expand Down
2 changes: 1 addition & 1 deletion MiniTwitch.Irc/Models/IrcChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ internal IrcChannel(ref IrcMessage message)
{
ReadOnlySpan<byte> tagKey = tag.Key.Span;
ReadOnlySpan<byte> tagValue = tag.Value.Span;
switch (tagKey.Sum())
switch (tagKey.MSum())
{
//r9k
case (int)Tags.R9K:
Expand Down
Loading

0 comments on commit d078b8f

Please sign in to comment.